Spring Boot 启动分析

220 阅读11分钟

Spring Boot 启动分析

启动分析那么之间找它的入口,我们在启动Spring Boot时候会指定一个Class,而这个Class一般都会带有@SpringBootApplication注解。

@SpringBootApplication

标识一个Configuration类-Spring boot启动配置(和@Configuration类一样特性),并且还触发自动装配(auto-configuration)和组件扫描(component scanning),等于@Configuration@EnableAutoConfiguratio @ComponentScan 。实际上它是一个混合注解。

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
   //排除加载的类
   @AliasFor(annotation = EnableAutoConfiguration.class)
   Class<?>[] exclude() default {};
   //排除加载的类名 
   @AliasFor(annotation = EnableAutoConfiguration.class)
   String[] excludeName() default {};
   //扫描的包名
   @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
   String[] scanBasePackages() default {};
   //扫描的类名
   @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
   Class<?>[] scanBasePackageClasses() default {};
   //bean名称生成器
   @AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
   Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
   //是否代理@Bean方法
   @AliasFor(annotation = Configuration.class)
   boolean proxyBeanMethods() default true;

}

ps:@Configuration@Component的区别?

@Configuration默认情况下为@Bean方法生代理,以拦截的对应的方法,而@Component仅仅标识为一个Spring Bean

1、@EnableAutoConfiguration

可以看到Import一个AutoConfigurationImportSelector@AutoConfigurationPackage

@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
   String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
   Class<?>[] exclude() default {};
   String[] excludeName() default {};
}

1.1、@AutoConfigurationPackage-自动装配扫包

自动配置所需要扫描的包,可以看到导入了AutoConfigurationPackages.Registrar,它实现了**ImportBeanDefinitionRegistrar接口(配置类解析时),也是说在BeanFactoryPostProcessor(允许对BeanDefinition进行修改和增加)中加载配置类的BeanDefinition时,会执行registerBeanDefinitions方法。根据源码,它会根据注解配置扫描对应的包并注册**BeanDefinition

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
   //配置扫描包名
   String[] basePackages() default {};
   //配置扫描class所在的包
   Class<?>[] basePackageClasses() default {};

}
//扫描包并注册
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        //根据注解信息扫描包
		register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
	}

	@Override
	public Set<Object> determineImports(AnnotationMetadata metadata) {
		return Collections.singleton(new PackageImports(metadata));
	}

}
1.1.1、自动配置具体扫描的包

默认情况下扫描**org.springframework.boot.autoconfigure**包下的class,具体扫包情况如下:

  1. @AutoConfigurationPackage 配置的basePackages
  2. @AutoConfigurationPackage 配置的basePackageClasses所对应的包
  3. 如果上面两个为空,则扫描注解所在的包
PackageImports(AnnotationMetadata metadata) {
   AnnotationAttributes attributes = AnnotationAttributes
         .fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false));
   List<String> packageNames = new ArrayList<>();
   for (String basePackage : attributes.getStringArray("basePackages")) {
      packageNames.add(basePackage);
   }
   for (Class<?> basePackageClass : attributes.getClassArray("basePackageClasses")) {
      packageNames.add(basePackageClass.getPackage().getName());
   }
   //为空即默认情况下扫描注解在包
   if (packageNames.isEmpty()) {
      packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));
   }
   this.packageNames = Collections.unmodifiableList(packageNames);
}

1.2、ImportSelector

自动装配注解还导入了AutoConfigurationImportSelector,它实现了DeferredImportSelector,那么在Import时也就是配置类解析的时候执行selectImports返回需要注册的类名,并递归解析返回的类名,即最终解析了selectImports返回的所有配置类。

通过源码我们可以看到具体是getAutoConfigurationEntry去获得需要自动装配的配置。

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
      ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

   private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
   //省略其他字段
   @Override
   public String[] selectImports(AnnotationMetadata annotationMetadata) {
      //判断是开启自动装配
      if (!isEnabled(annotationMetadata)) {
         return NO_IMPORTS;
      }
       //获得自动装配的Entry
      AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
      return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
   }
   //
   protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
      if (!isEnabled(annotationMetadata)) {
         return EMPTY_ENTRY;
      }
      AnnotationAttributes attributes = getAttributes(annotationMetadata);
      //1、spi读取所有候选Class
      List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
      //2、去重
      configurations = removeDuplicates(configurations);
      //3、排除配置 
      Set<String> exclusions = getExclusions(annotationMetadata, attributes);
      checkExcludedClasses(configurations, exclusions);
      configurations.removeAll(exclusions);
      //4、条件装配
      configurations = getConfigurationClassFilter().filter(configurations);
      //5、触发import事件 
      fireAutoConfigurationImportEvents(configurations, exclusions);
      return new AutoConfigurationEntry(configurations, exclusions);
   }
   //省略其他
 }
(1)SPI机制读取候选的自动装配类名

在getCandidateConfigurations中,通过**SpringFactoriesLoader也就是SPI**机制读取META-INF/spring.factories文件中key=org.springframework.boot.autoconfigure.EnableAutoConfiguration所对应的Class Name;

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
   List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
         getBeanClassLoader());
   Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
         + "are using a custom packaging, make sure that file is correct.");
   return configurations;
}
//返回自动装配读取的key所对应的类
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
}
//根据key=全类名,SPI机制读取文件中对应配置类  即此时key=org.springframework.boot.autoconfigure.EnableAutoConfiguration
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		String factoryTypeName = factoryType.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
//SpringFactoriesLoader 所读取的资源路径
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
(2)去重

通过LinkedHashSet去重

protected final <T> List<T> removeDuplicates(List<T> list) {
   return new ArrayList<>(new LinkedHashSet<>(list));
}
(3)排除配置的exclusions

获得注解中设置的需要排除的类名,并检查是否存在需要排除Class是否存在,不存在即为无效存在无效抛出异常。最后排除掉配置的exclusions

//获得注解设置的需要排除的类名
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
//检查排除的配置是否在存在,即所以的排除类需要存在
checkExcludedClasses(configurations, exclusions);
//删除这些类
configurations.removeAll(exclusions);
(4)过滤

一些资源可能需要一些前置资源,例如Mybaits就就需要DataSource所以Spring Boot提供了条件装配机制。在ConfigurationClassFilter中根据**AutoConfigurationImportFilter以及autoConfigurationMetadata自动装配元数据对候选的class进行过滤,也就是根据@Conditionalspring-autoconfigure-metadata.properties配置装配条件挑出能够装配**的Class Name。

Conditional

SPI机制加载AutoConfigurationImportFilter对候选的Class进行Conditional过滤。例如OnBeanCondition,OnClassCondition等这些条件配置过滤。针对不同维度分为以下三种Conditional

# Auto Configuration Import Filters 自动装配过滤
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
private ConfigurationClassFilter getConfigurationClassFilter() {
	if (this.configurationClassFilter == null) {
        //SPI机制加载`AutoConfigurationImportFilter`
		List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
		for (AutoConfigurationImportFilter filter : filters) {
			invokeAwareMethods(filter);
		}
        //创建ConfigurationClassFilter
		this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
	}
	return this.configurationClassFilter;
}
//SPI机制加载`AutoConfigurationImportFilter`
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
	return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter`.class, this.beanClassLoader);
}
  1. OnBeanCondition

    用于检查特定的bean是否存在,对应以下注解:

    • @ConditionalOnBean

      当指定类的Bean存在时,进行装配

    • ``@ConditionalOnMissingBean`

      当指定类的Bean不存在时,进行装配

    • @ConditionalOnSingleCandidate这些注解

      当指定类的Bean存在且为单例,进行装配

  2. OnClassCondition

    OnBeanCondition类似,不过针对是Class 是否存在,对应以下注解:

    • @ConditionalOnClass

      当指定类存在时,进行装配

    • ``@ConditionalOnMissingClass`

      当指定类不存在时,进行装配

  3. OnWebApplicationCondition

    用于检查是否在Web容器中,对应以下注解

    • @ConditionalOnWebApplication

      当SpringApplication是Web容器

    • ``@ConditionalOnNotWebApplication`

      当SpringApplication不是Web容器

  4. 其他Condition

spring-autoconfigure-metadata

因为提供很多Starter不同的Starter,所以它也提供了这些自动的条件,根据源码可以看到读取了META-INF/spring-autoconfigure-metadata.properties这一配置文件,里面配置了各个类的装配的条件。

protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";

static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
	return loadMetadata(classLoader, PATH);
}
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
	try {
		Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path): ClassLoader.getSystemResources(path);
		Properties properties = new Properties();
		while (urls.hasMoreElements()) {
			properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
		}
		return loadMetadata(properties);
	}
	catch (IOException ex) {
		throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
	}
}
//摘取的配置元数据片段 
//表示CacheAutoConfiguration必须在CacheAspectSupport这个bean存在时才生效
xxx.CacheAutoConfiguration.ConditionalOnBean=xxx.CacheAspectSupport
//表示CacheAutoConfiguration必须在CacheManager这个类存在时才生效
xxx.CacheAutoConfiguration.ConditionalOnClass=xxx.CacheManager
(5)发表自动装配导入事件

SPI机制加载AutoConfigurationImportListener,并触发自动装配导入事件

private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
   //SPI机制加载AutoConfigurationImportListener
   List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
   if (!listeners.isEmpty()) {
      AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
      for (AutoConfigurationImportListener listener : listeners) {
         invokeAwareMethods(listener);
         //触发监听器的import事件
         listener.onAutoConfigurationImportEvent(event);
      }
   }
}

2、 @ComponentScan-组件扫描

ConfigurationClassPostProcessor这一BeanFactory后处理器中对配置类进行解析(ConfigurationClassParser),而在配置类的解析过程中如果发现配置类中存在@ComponentScan则根据这个注解配置(ComponentScanAnnotationParser解析注解)进行扫包ClassPathBeanDefinitionScanner进行扫包),对每个扫描出来的BeanDefinition当做配置类解析。

ClassPathBeanDefinitionScanner.doScan默认情况下扫描以下三种注解标识的类:

  • @Component

    Spring 提供的标识一个Bean

  • @ManagedBean

    JSR-250

  • @Named

    JSR-330

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
   Assert.notEmpty(basePackages, "At least one base package must be specified");
   Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
   //遍历所有的包路径
   for (String basePackage : basePackages) {
       //获得所有的候选 即扫描Class path下的 class
      Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
      for (BeanDefinition candidate : candidates) {
         ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
         candidate.setScope(scopeMetadata.getScopeName());
         String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
         if (candidate instanceof AbstractBeanDefinition) {
            postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
         }
         if (candidate instanceof AnnotatedBeanDefinition) {
            AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
         }
         if (checkCandidate(beanName, candidate)) {
            BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
            definitionHolder =
                  AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            beanDefinitions.add(definitionHolder);
            registerBeanDefinition(definitionHolder, this.registry);
         }
      }
   }
   return beanDefinitions;
}
//通过过滤器来判断哪些是符合候选的
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
	for (TypeFilter tf : this.excludeFilters) {
		if (tf.match(metadataReader, getMetadataReaderFactory())) {
			return false;
		}
	}
	for (TypeFilter tf : this.includeFilters) {
		if (tf.match(metadataReader, getMetadataReaderFactory())) {
			return isConditionMatch(metadataReader);
		}
	}
	return false;
}
//注册默认过滤器
protected void registerDefaultFilters() {
    //@Component是候选的
	this.includeFilters.add(new AnnotationTypeFilter(Component.class));
	ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
	try {
        //@ManagedBean 是候选的
		this.includeFilters.add(new AnnotationTypeFilter(
					((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
		logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
	}
	catch (ClassNotFoundException ex) {
			// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
	}
	try {
        //@Named是候选的
		this.includeFilters.add(new AnnotationTypeFilter(
					((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
		logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
	}
	catch (ClassNotFoundException ex) {
			// JSR-330 API not available - simply skip.
	}
}

3、自动装配是在什么时候生效的

上文可以是自动装配和组件扫描其实就是利用@Import注册了AutoConfigurationImportSelectorAutoConfigurationImportSelector那是怎么生效的呢?

答案:在ConfigurationClassPostProcessor中(Spring 刷新流程中的postBeanFactorPostProcessor-允许我们对已有BeanDefinition做修改或者增加),它会递归解析配置类对应些@ImportImportSelector接口、@Bean方法等等都会去解析并注册到Spring中,此时不就生效了么。其实大多数需要扫描都是通过BeanFactorPostProcessor或者已有BeanFactorPostProcessor提供的功能实现。例如@MapperScan

//配置类解析代码
protected final SourceClass doProcessConfigurationClass(
      ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
      throws IOException {

   if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
      // Recursively process any member (nested) classes first
      processMemberClasses(configClass, sourceClass, filter);
   }

   // Process any @PropertySource annotations 读取@PropertySource指定的配置文件并到这个
   for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), PropertySources.class,
         org.springframework.context.annotation.PropertySource.class)) {
      if (this.environment instanceof ConfigurableEnvironment) {
         processPropertySource(propertySource);
      }
      else {
         logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
               "]. Reason: Environment must implement ConfigurableEnvironment");
      }
   }

   // Process any @ComponentScan annotations 组件扫描注册
   Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
   if (!componentScans.isEmpty() &&
         !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
      for (AnnotationAttributes componentScan : componentScans) {
         // The config class is annotated with @ComponentScan -> perform the scan immediately
         Set<BeanDefinitionHolder> scannedBeanDefinitions =
               this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
         // Check the set of scanned definitions for any further config classes and parse recursively if needed
         for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
            BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
            if (bdCand == null) {
               bdCand = holder.getBeanDefinition();
            }
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
               parse(bdCand.getBeanClassName(), holder.getBeanName());
            }
         }
      }
   }

   // Process any @Import annotations  @Import注解 包含ImportBeanDefinitionRegistrar
   processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

   // Process any @ImportResource annotations  @ImportResource 导入的xml配置
   AnnotationAttributes importResource =
         AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
   if (importResource != null) {
      String[] resources = importResource.getStringArray("locations");
      Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
      for (String resource : resources) {
         String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
         configClass.addImportedResource(resolvedResource, readerClass);
      }
   }
 
   // Process individual @Bean methods @Bean方法
   Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
   for (MethodMetadata methodMetadata : beanMethods) {
      configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
   }

   // Process default methods on interfaces
   processInterfaces(configClass, sourceClass);

   // Process superclass, if any
   if (sourceClass.getMetadata().hasSuperClass()) {
      String superclass = sourceClass.getMetadata().getSuperClassName();
      if (superclass != null && !superclass.startsWith("java") &&
            !this.knownSuperclasses.containsKey(superclass)) {
         this.knownSuperclasses.put(superclass, configClass);
         // Superclass found, return its annotation metadata and recurse
         return sourceClass.getSuperClass();
      }
   }

   // No superclass -> processing is complete
   return null;
}

启动流程

SpringApplication.run(primarySource, args)为Spring boot 启动的入口

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
	return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    //1、创建SpringApplication
    //2、调用SpringApplication.run(args)方法
	return new SpringApplication(primarySources).run(args);
}

1、实例化-SpringApplication

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   this.resourceLoader = resourceLoader;
   Assert.notNull(primarySources, "PrimarySources must not be null");
   //需要在spring 刷新先注册的字段
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
   //加载ApplicationContextInitializer用于初始化上下文
   setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
   //ApplicationListener 用于接收启动流程中广播的事件
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   this.mainApplicationClass = deduceMainApplicationClass();
}

(1) 收集对应的primarySources

primarySources个人理解为启动配置配置类,是后面上下文配置类递归解析的入口。primarySources类上并不一定需要@SpringApplication注解。@SpringApplication只是一个复合注解方便快速配置,只要不嫌麻烦我们甚至不使用这个注解,或者在多个类上使用改注解。

(2) 决定应用类型

​ 根据classPath中是否包含一些特定的Class来判断web应用类型,后面在创建上下文时需要根据应用类型来创建,web应用类型分以下三种:

  • NONE

    非web应用

  • SERVLET

    基于servletweb应用,例如依赖了Spring MVC

  • REACTIVE

    REACTIVE式Web应用,例如Spring Reactive

(3) SPI机制ApplicationContextInitializer

​ 通过SpringFactoriesLoader加载 "META-INF/spring.factories"下 key=org.springframework.context.ApplicationContextInitializer的类,也就是SPI机制加载上下文初始化器。用于在ApplicationContext刷新前(prepareContext)执行,ApplicationContext进行初始化操作

(4) SPI机制加载ApplicationListener

​ 和步骤(3)一样的加载方式,只不过此处是加载ApplicationListener应用监听器。用于处理EventPublishingRunListener广播出来的事件

ps:为什么Spring 提供了事件机制,Spring Boot 为什么要重新弄一份事件机制?

答案:在Spring Boot启动流程中Spring 容器的启动只占据一部分,所以需要新的事件机制。

(5) 找到MainClass

main方法所在的类

2、运行SpringApplication

在run方法中可以看到运行的主体流程

public ConfigurableApplicationContext run(String... args) { 
   //计时器
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   configureHeadlessProperty();
   //1、加载并启动 运行时监听器 
   SpringApplicationRunListeners listeners = getRunListeners(args);
   //广播正在启动事件 
   listeners.starting();
   try {
     
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      //2、创建环境 
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
      //配置需要忽略的Bean
      configureIgnoreBeanInfo(environment);
      // 打印banner
      Banner printedBanner = printBanner(environment);
      //3、创建上下文 
      context = createApplicationContext();
      //异常报告 
      exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
      //4、准备上下文  主要将环境以及参数还有启动配置类注册到上线文中
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);
      //5、刷新上下文
      refreshContext(context);
      //钩子函数允许子类进行扩展 
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
      //广播启动完成事件
      listeners.started(context);
      //6、调用runner  即实现ApplicationRunner和 CommandLineRunner
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      //启动失败处理
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }

   try {
       //广播允许中
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

(1)获取并启动运行监听器-SpringApplicationRunListener

用于Spring Boot 启动流程各个阶段事件的广播以提供扩展机制-作用于不同阶段。因为ApplicationContext refresh之前,其事件机制还未初始化(refresh中初始化了事件机制)所以无法用于Spring boot Application启动流程中使用,所以才需要补充(对应Spring 事件机制的补充)。 创建了一个SpringApplicationRunListeners,里面包含了SpringFactoriesLoader(SPI)机制读取的"META-INF/spring.factories"下的 key=org.springframework.boot.SpringApplicationRunListener所指定的监听器。默认情况下只有EventPublishingRunListener。Spring Boot 在启动过程中依次会广播(EventPublishingRunListener)以下事件。

  1. start()

    开始启动时

    ApplicationStartingEvent
    
  2. environmentPrepared(ConfigurableEnvironment environment)

    环境准备完成 prepareEnvironment

    ApplicationEnvironmentPreparedEvent
    
  3. contextPrepared(ConfigurableApplicationContext context)

    **ApplicationContextInitializer**执行完毕后

    ApplicationContextInitializedEvent
    
  4. contextLoaded(ConfigurableApplicationContext context)

    在注册Source后也就是Spring Boot启动配置类后

    ApplicationPreparedEvent
    
  5. started(ConfigurableApplicationContext context)

    容器刷新完成

    ApplicationStartedEvent
    
  6. running(ConfigurableApplicationContext context)

    刷新完成并执行runner后

    ApplicationReadyEvent
    
  7. failed(ConfigurableApplicationContext context, Throwable exception)

    捕获到Trowable

    ApplicationFailedEvent
    
//获取运行监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
//启动监听器
listeners.starting();
//---------------------------------------
//创建了一个SpringApplicationRunListeners
private SpringApplicationRunListeners getRunListeners(String[] args) {
	Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
	return new SpringApplicationRunListeners(logger,
                //SPi机制读取SpringApplicationRunListener全类名指定的监听器
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
EventPublishingRunListener-广播给ApplicationListener

在广播事件时,遍历在创建SpringApplication时SPI所加载的**ApplicationListener进行事件广播。其原理就是通过ApplicationEventMulticaster**事件广播器(此时是SimpleApplicationEventMulticaster)广播事件。

public EventPublishingRunListener(SpringApplication application, String[] args) {
   this.application = application;
   this.args = args;
   this.initialMulticaster = new SimpleApplicationEventMulticaster();
   for (ApplicationListener<?> listener : application.getListeners()) {
      this.initialMulticaster.addApplicationListener(listener);
   }
}

ApplicationEventMulticaster用于管理ApplicationListener并广播事件(ApplicationEvent

(2)创建运行时环境

创建环境(PropertySources以及Profiles)并对环境进行配置,并绑定到SpringApplication

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments) {
   // Create and configure the environment  创建环境
   ConfigurableEnvironment environment = getOrCreateEnvironment();
    //配置环境
   configureEnvironment(environment, applicationArguments.getSourceArgs());
   ConfigurationPropertySources.attach(environment);
   //广播环境已经准备好事件
   listeners.environmentPrepared(environment);
   //绑定到 SpringApplication
   bindToSpringApplication(environment);
   //不是web环境的化就进行转化
   if (!this.isCustomEnvironment) {
      environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
            deduceEnvironmentClass());
   }
   //将ConfigurationPropertySource附加到environment上
   ConfigurationPropertySources.attach(environment);
   return environment;
}

(3)创建ApplicationContext

按照指定applicationContextClass去创建上下文(也就是Spring 容器),如果没有指定则根据**webApplicationType也就是容器类型创建不同ApplicationContext**,对应关系如下:

  • SERVLET

    AnnotationConfigServletWebServerApplicationContext
    
  • REACTIVE

    AnnotationConfigReactiveWebServerApplicationContext
    
  • 其他

    AnnotationConfigApplicationContext
    
context = createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
    //得到指定的contextClass
	Class<?> contextClass = this.applicationContextClass;
    //没有指定的话则按类型创建
	if (contextClass == null) {
		try {
			switch (this.webApplicationType) {
			case SERVLET:
				contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
				break;
			case REACTIVE:
				contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
				break;
			default:
				contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
			}
		}
		catch (ClassNotFoundException ex) {
			throw new IllegalStateException(
					"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
		}
	}
	return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

(4)准备ApplicationContext!!!

容器 refresh前的准备工作,对上下文进行配置。特别注意**ApplicationContextInitializer-在刷新之前的回调接口**,完成一些初始化操作针。最后将启动类primarySourcessetSources注册到context中,也就是说将Spring 将以这些启动配置类为入口逐渐解析并加载所配置BeanDefinition。这一步最为重要可以看到Spring Boot 是怎么和Spring 联系起来的-将environment、启动配置类(sources)、参数等等注册到ConfigurableApplicationContext 中。

prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//-------------刷新前的准备一些初始化操作-------------
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
      SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
   //设置环境
   context.setEnvironment(environment);
   //后置处理上下文 允许子类做扩展 
   postProcessApplicationContext(context);
   //执行上下文初始化器ApplicationContextInitializer 也就是创建SpringApplication时SPI机制读取那些初始化器
   applyInitializers(context);
   //广播上下文已经准备好了事件
   listeners.contextPrepared(context);
   //记录启动日志
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }
   // Add boot specific singleton beans 注册Spring boot参数、springBootBanner
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   //注册参数
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   if (printedBanner != null) {
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   //设置BeanDefinition是否可以重写
   if (beanFactory instanceof DefaultListableBeanFactory) {
      ((DefaultListableBeanFactory) beanFactory)
            .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   //配置懒加载的BeanFactory后置处理器
   if (this.lazyInitialization) {
      context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
   }
   // Load the sources
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   //最为重要的一 将sources加载进Context 也就是将启动类 注册到上下文!!!!
   load(context, sources.toArray(new Object[0]));
   //广播context已经load完毕时间
   listeners.contextLoaded(context);
}

public Set<Object> getAllSources() {
	Set<Object> allSources = new LinkedHashSet<>();
	if (!CollectionUtils.isEmpty(this.primarySources)) {
			allSources.addAll(this.primarySources);
	}
	if (!CollectionUtils.isEmpty(this.sources)) {
			allSources.addAll(this.sources);
	}
	return Collections.unmodifiableSet(allSources);
}

(5)刷新上下文-启动容器

开始初始化Spring容器,在此之间注册了ShutdownHook也就是在JVM运行时注册一个钩子函数-当JVM关机关闭容器

refreshContext(context);

private void refreshContext(ConfigurableApplicationContext context) {
	if (this.registerShutdownHook) {
		try {
            //注册ShutdownHook
			context.registerShutdownHook();
		}
		catch (AccessControlException ex) {
			// Not allowed in some environments.
		}
	}
    //刷新Spring容器 也就是启动Spring容器
	refresh((ApplicationContext) context);
}
protected void refresh(ConfigurableApplicationContext applicationContext) {
    //初始化(启动)Spring容器入口
	applicationContext.refresh();
}

(6)容器刷新后置处理

用于容器刷新后调用,如果需要特殊的刷新后处理,则重写该方法,用于子类的扩展

protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
    //子类扩展
}

(7)执行Runners-启动后扩展点

Spring Boot 提供了一种机制用于在Spring Boot程序启动后执行一次性的特定程序。分为以下两种

  • ApplicationRunner
  • CommandLineRunner

此时在Spring启动后,所以从容器中取出ApplicationRunnerCommandLineRunner,排序后遍历执行。也是Spring Boot 提供的扩展点

callRunners(context, applicationArguments);
private void callRunners(ApplicationContext context, ApplicationArguments args) {
    //获取所有的runners
	List<Object> runners = new ArrayList<>();
	runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
	runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    //根据Order排序
	AnnotationAwareOrderComparator.sort(runners);
	for (Object runner : new LinkedHashSet<>(runners)) {
		if (runner instanceof ApplicationRunner) {
			callRunner((ApplicationRunner) runner, args);
		}
		if (runner instanceof CommandLineRunner) {
			callRunner((CommandLineRunner) runner, args);
		}
	}
}

总结

@SpringBootApplication只是一个复合注解,可以快速配置Spring Boot启动所需要的配置 最终注册到Spring中,什么自动装配、组件扫描等等实际生效还是在Spring 中。

Spring Boot基于约定大于配置理念以及自动装配,让我们可以快速开发一个Spring 应用,而Spring的目的是简化开发,Spring Boot可以认为是Spring 的脚手架。