SpringBoot启动流程的源码分析

前言

Spring Boot 版本 2.1.7.RELEASE
spring-cloud-dependencies 版本 Greenwich.SR5
本文大致跟踪了一遍Spring Boot的启动流程。
请边debug源代码边看本文,因为很多变量细节在debug时才能清晰的观察到,而本文是无法全部涵盖所有细节的。
文中如有错误遗漏,请留言指正。

相关文章

SpringBoot中Tomcat的源码分析

SpringBoot启动流程

程序入口

public static void main(String[] args) {
    SpringApplication.run(XXXApplication.class, args);
}

SpringApplication.run()函数内部

return new SpringApplication(primarySources).run(args); //primarySources is XXXApplication.class

1. SpringApplication的构造函数内部

this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();

1.1 WebApplicationType.deduceFromClasspath() 根据org.springframework.web.reactive.DispatcherHandler javax.servlet.Servlet org.springframework.web.context.ConfigurableWebApplicationContext等类是否存在来推断服务器类型。类型包括WebApplicationType.REACTIVE WebApplicationType.SERVLET WebApplicationType.NONE

1.2 setInitializers()setListeners()函数为SpringApplication的成员变量initializerslisteners赋值。二者均使用getSpringFactoriesInstances(XXX.class)方法获取参数。
1.2.1 getSpringFactoriesInstances(XXX.class)方法中,参数XXX.class是一个interface。此方法首先会根据接口类的类名,获取其对应的多个实现类,然后实例化这些实现类,最后返回这些实例。这些实现类就是SpringFactories,是各个jar包中的factory。
getSpringFactoriesInstances()方法内部通过SpringFactoriesLoader.loadFactoryNames()方法加载。方法内部会在classpath下寻找路径为META-INF/spring.factories的所有文件,此文件的内容格式为properties,文件中的每一行记录一条interface及其对应的多个实现类。每行的内容形如
example.MyService=example.MyServiceImpl1,example.MyServiceImpl2
根据所有的spring.factories文件,可以构建出一个mapMultiValueMap<String, String>,其中key为interface,value为其对应的多个实现类组成的list。这个map会存到Map<ClassLoader, MultiValueMap<String, String>> cache中,方便未来多次查找。
spring.factories的加载类似于工具类,没有确切的加载时机。随时用到随时加载,只是首次加载时,扫描到的spring.factories全部放入缓存,后续读取缓存。

1.2 续
通过getSpringFactoriesInstances()方法的搜索, SpringApplication实例的成员变量得到了赋值。initializers类型为List,包含了ApplicationContextInitializer.class的多个实现类;listeners类型为List,包含了ApplicationListener.class的多个实现类。
其中BootstrapApplicationListener就是在此时被加入到listeners中的。

2 SpringApplication结束实例化后,会调用成员方法run()run()中有一些关键步骤
2.1 SpringApplicationRunListeners listeners = getRunListeners(args);
2.1.1 此方法首先使用 1.2.1 中提到的getSpringFactoriesInstances(XXX.class)方法搜索SpringApplicationRunListener.class的实现类。
SpringApplicationRunListener interface 定义了SpringApplication.run()运行过程中会抛出的事件,(这个接口类的命名也可以看出SpringApplication - Run - Listener)。

public interface SpringApplicationRunListener {
    void starting();
    void environmentPrepared(ConfigurableEnvironment environment);
    void contextPrepared(ConfigurableApplicationContext context);
    void contextLoaded(ConfigurableApplicationContext context);
    void started(ConfigurableApplicationContext context);
    void running(ConfigurableApplicationContext context);
    void failed(ConfigurableApplicationContext context, Throwable exception);
}

这些事件都是SpringApplicationEvent类的子类,后文将会提到这些事件。
SpringApplicationRunListener接口的实现类SpringApplicationRunListener将会被搜索出来并实例化。
SpringApplicationRunListener类内部实例化了一个ApplicationEventMulticaster成员变量,即SimpleApplicationEventMulticaster类。将来这个multicaster会用来抛出各个事件。将这个multicaster实例化完成后,又将SpringApplication.listeners填入到这个multicaster内部,作为其初始值,代码如下。

        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        for (ApplicationListener<?> listener : application.getListeners()) {
            this.initialMulticaster.addApplicationListener(listener);
        }

也就是说META-INF/spring.factories文件中定义的所有ApplicationListener.class的实现类都会监听SpringApplicationEvent事件。
2.1.2 最终getRunListeners(args)方法会将多个SpringApplicationRunListener接口的实现类封装成SpringApplicationRunListeners,然后返回。

2.2 listeners.starting();
SpringApplicationRunListener调用starting()方法,抛出ApplicationStartingEvent事件。
2.2.1 SimpleApplicationEventMulticaster类负责抛出事件。
2.2.1.1 SimpleApplicationEventMulticaster会首先调用getApplicationListeners()方法获取监听事件的所有listener。由于SimpleApplicationEventMulticaster类的初始值listener包括了SpringApplication.listeners(见2.1.1的末尾),所以这些listener会收到所有SpringApplicationEvent事件。
getApplicationListeners()中会使用AbstractApplicationEventMulticaster.supportsEvent()方法来筛选监听某事件的listener。
筛选出的listener会放入multicaster的retrieverCache中,以备将来再次查询。
2.2.1.2 SimpleApplicationEventMulticaster拿到所有的listener后,对每一个listener调用invokeListener(listener, event);。各个listener会通过ApplicationListener.onApplicationEvent(E event)方法实现自己的事件响应逻辑。

2.3 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); 此方法执行了以下操作。
2.3.1 ConfigurableEnvironment environment = getOrCreateEnvironment();
此方法根据 1.1 中推断出的服务器类型,实例化ConfigurableEnvironment类。

switch (this.webApplicationType) {
        case SERVLET:
            return new StandardServletEnvironment();
        case REACTIVE:
            return new StandardReactiveWebEnvironment();
        default:
            return new StandardEnvironment();
        }

environment 的构造函数中确定propertySources内的Property源顺序,并初始化部分Property源。具体见各Environment类的构造函数代码。
StandardServletEnvironment为例。Property源的顺序为servletConfigInitParams servletContextInitParams systemProperties systemEnvironment。其中前两个servletConfigInitParams servletContextInitParamsStubPropertySource,这是一个没有任何内容的占位Property源,为了确定它们的顺序,之后会被替掉。systemProperties systemEnvironment Property源在这里初始化的,分别是System.getProperties()System.getenv()

2.3.2 configureEnvironment(environment, applicationArguments.getSourceArgs());
本文以StandardServletEnvironment为例。configureEnvironment()包含以下步骤。
首先,

ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);

为environment设置了ApplicationConversionService实例,用于一些数据格式的转换。
2.3.2.1 configurePropertySources(environment, args);
environment 在 propertySources 列表的首位放置 commandLineArgs PropertySource,数据源是命令行启动参数

2.3.2.2 configureProfiles(environment, args);
从上一步 2.3.2.1 配置的各个Property源中寻找当前激活的Profile
property key 为"spring.profiles.active"
2.3.2.1中的数据源中没有"spring.profiles.active"的话,本步骤结束后,activeProfiles依旧为空。

2.3.3 listeners.environmentPrepared(environment);
发布ApplicationEnvironmentPreparedEvent事件,以下监听器进行响应。
org.springframework.cloud.bootstrap.BootstrapApplicationListener
org.springframework.cloud.bootstrap.LoggingSystemShutdownListener
org.springframework.boot.context.config.ConfigFileApplicationListener
org.springframework.boot.context.config.AnsiOutputApplicationListener
org.springframework.boot.context.logging.LoggingApplicationListener
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener
org.springframework.boot.autoconfigure.BackgroundPreinitializer
org.springframework.boot.context.config.DelegatingApplicationListener
org.springframework.boot.context.FileEncodingApplicationListener

2.3.3.1 BootstrapApplicationListener
BootstrapApplicationListener响应ApplicationEnvironmentPreparedEvent事件。
2.3.3.1.1
此listener内部会new一个 bootstrapEnvironment,类为StandardEnvironment,与主流程的StandardServletEnvironment相互独立。创建一个SpringApplication和一个名为bootstrapConfigurableApplicationContextrun()起来。

bootstrapEnvironment 的 PropertySources 是原Environment中过滤出有实际配置的PropertySource列表,并在开头加上一个名为 bootstrap 的PropertySource,里边是硬编码的两个配置。
"spring.config.name" -> "bootstrap"
"spring.main.web-application-type" -> "none"

基于 bootstrapEnvironment 构建一个新的 SpringApplication,我们暂称之为 bootstrapSpringApplication。

它使用原 Environment 的 profile。先列举一些在执行 run 函数之前,bootstrapSpringApplication 与 原SpringApplication 的区别。

bootstrapSpringApplication 原SpringApplication 描述
SpringApplication#environment bootstrapEnvironment 目前为null,之后是原Environment
SpringApplication#primarySources BootstrapImportSelectorConfiguration 具有main函数的被@SpringBootApplication注解的那个应用程序入口类
SpringApplication#webApplicationType NONE SERVLET 影响createApplicationContext()创建不同的context。原SpringApplication会创建tomcat,而 bootstrapSpringApplication 不会。
SpringApplication#bannerMode OFF CONSOLE 打印banner
SpringApplication#logStartupInfo false true 打印一些启动信息
SpringApplication#registerShutdownHook false true 创建关闭钩子
SpringApplication#additionalProfiles 原 Environment 的 profile 目前为空
SpringApplication#isCustomEnvironment true false

后续执行过程分辨bootstrap和原应用程序重要类的方法

字段 bootstrap 原应用程序
SpringApplication SpringApplication#primarySources BootstrapImportSelectorConfiguration 具有main函数的被@SpringBootApplication注解的那个应用程序入口类
ApplicationContext ApplicationContext#id 先为 "application",后为"bootstrap" "application-1"

然后 bootstrapSpringApplication 开始执行 run 函数。这几乎相当于从第一步又重新执行一遍,直至执行到末尾,后半部分执行逻辑在后文才会描述。

(不过此时的id字段还没有设置为bootstraprun()执行完成后才会设置,而且在程序入口的那个ConfigurableApplicationContextprepareContext阶段中,这个名为"bootstrap"ConfigurableApplicationContext会作为应用的ConfigurableApplicationContext的父级)

这个名为"bootstrap"ConfigurableApplicationContext会有一些特殊配置,比如。再次执行run()的过程中还会触发BootstrapApplicationListener,不过当发现已经引入名为bootstrapPropertySource时,就不再执行后续代码了,避免了陷入不断触发BootstrapApplicationListener的死循环。

在这个名为bootstrap的独立的环境run()的过程中,通过硬编码的方式加载bootstrap配置文件(如:bootstrap.yml)。
相关代码通过这个字段可查到。org.springframework.cloud.bootstrap.BootstrapApplicationListener#BOOTSTRAP_PROPERTY_SOURCE_NAME = "bootstrap"
org.springframework.cloud.bootstrap.BootstrapApplicationListener#bootstrapServiceContext方法中,通过硬编码的方式增加spring.config.name = bootstrap配置。
然后在加载配置文件过程中的org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#getSearchNames方法通过条件判断,选择加载bootstrap配置。下一节2.3.3.2中会在这个方法的判断中,选择加载application配置。

2.3.3.1.2 bootstrapSpringApplication.run()结束后
bootstrapSpringApplication.run() 返回 AnnotationConfigApplicationContextAnnotationConfigApplicationContext的id为"bootstrap"
原SpringApplication添加一个新的ApplicationContextInitializer,类为BootstrapApplicationListener.AncestorInitializer,它里面保存着bootstrap ApplicationContext。
bootstrapEnvironment 的PropertySources移除 bootstrap PropertySource。
bootstrapEnvironment 和 原 Environment的PropertySources 列表末尾都添加一个 defaultProperties PropertySource,他的sources字段中保存着 bootstrap 配置文件 PropertySource。

从"bootstrap"AnnotationConfigApplicationContext中获取实现了ApplicationContextInitializer接口的bean,把他们加入到原SpringApplication.initializers的末尾,最终依次新增PropertySourceBootstrapConfigurationEnvironmentDecryptApplicationInitializer。再单独新增一个DelegatingEnvironmentDecryptApplicationInitializer

2.3.3.2 ConfigFileApplicationListener
SpringApplicationApplicationEnvironmentPreparedEvent事件的监听器继续执行。
ConfigFileApplicationListener#onApplicationEnvironmentPreparedEvent
通过spring.factories加载EnvironmentPostProcessor,加载出以下4个。

org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor
org.springframework.cloud.client.HostInfoEnvironmentPostProcessor

再加上当前的ConfigFileApplicationListener,排序后依次调用EnvironmentPostProcessor#postProcessEnvironment函数。

2.3.3.2.1 SystemEnvironmentPropertySourceEnvironmentPostProcessor
将 systemEnvironment PropertySource 从SystemEnvironmentPropertySource重新封装为 OriginAwareSystemEnvironmentPropertySource

2.3.3.2.2 HostInfoEnvironmentPostProcessor
这里会调用一下ConfigurationPropertySources.attach(environment);,生成 propertySources PropertySource,具体逻辑后文会描述。
末尾增加 springCloudClientHostInfo PropertySource

2.3.3.2.3 ConfigFileApplicationListener
ConfigFileApplicationListener#postProcessEnvironment
增加 random PropertySource

new Loader(environment, resourceLoader).load();
如同2.3.3.1.2小节描述的。
new Loader(environment, resourceLoader)
通过spring.factories 加载 PropertySourceLoader,分别是PropertiesPropertySourceLoaderYamlPropertySourceLoader

ConfigFileApplicationListener.Loader#load()
从environment中查找"spring.profiles.active"和"spring.profiles.include"配置,添加activeProfiles和profiles。
搜索配置,默认搜索路径ConfigFileApplicationListener#DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/",默认文件名ConfigFileApplicationListener#DEFAULT_NAMES = "application",使用PropertiesPropertySourceLoaderYamlPropertySourceLoader加载配置文件内容,生成PropertySource。关键代码是
List<PropertySource<?>> loaded = loader.load(name, resource);
配置文件路径名举例"classpath:/application.yml" "classpath:/application-dev.yml"
加载好的配置文件会被封装成PropertySources,并存储在org.springframework.boot.context.config.ConfigFileApplicationListener.Loader.loaded成员变量中。
最后将ConfigFileApplicationListener.Loader.loaded中的所有PropertySource加入到PropertySources中。

2.3.4 bindToSpringApplication
重新回到prepareEnvironment函数内部。
SpringApplication#bindToSpringApplication根据"spring.main"配置设置SpringApplication字段

2.3.5 ConfigurationPropertySources.attach(environment);
当前版本中这段逻辑出现在这里,高版本中这段逻辑会在listeners.environmentPrepared(environment);之前。
如果不存在名为 configurationProperties 的 propertySource。则将当前的propertySources整体打包为SpringConfigurationPropertySources,命名为 configurationProperties。放到environment.propertySources的第一个。

2.4 回到应用程序的SpringApplication中。
Banner printedBanner = printBanner(environment);
打印Banner,就是在控制台里打印那个SpringBoot图形。

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.7.RELEASE)

2.5 context = createApplicationContext();
根据webApplicationType实例化ConfigurableApplicationContext
如,SERVLET类型对应的class是org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext

ConfigurableApplicationContext 的构造函数中会
实例化 DefaultListableBeanFactory,并设置到 GenericApplicationContext#beanFactory字段中
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);函数会加入一些 BeanDefinition。
ConfigurationClassPostProcessor
AutowiredAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor
EventListenerMethodProcessor
DefaultEventListenerFactory

2.6 prepareContext(context, environment, listeners, applicationArguments, printedBanner);
以下是方法内的一些关键步骤。
2.6.1 applyInitializers(context);
使用SpringApplication.initializers内的各个initializer(见1.2),对context进行初始化。

排序后的initializers列表为
org.springframework.cloud.bootstrap.BootstrapApplicationListener$AncestorInitializer
org.springframework.cloud.bootstrap.BootstrapApplicationListener$DelegatingEnvironmentDecryptApplicationInitializer
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration$$EnhancerBySpringCGLIB$$6ca98a34
org.springframework.cloud.bootstrap.encrypt.EnvironmentDecryptApplicationInitializer
org.springframework.boot.context.config.DelegatingApplicationContextInitializer
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer
org.springframework.boot.context.ContextIdApplicationContextInitializer
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

org.springframework.cloud.bootstrap.BootstrapApplicationListener$AncestorInitializer
将 bootstrap.yml 配置文件的配置源 从 defaultProperties 中提出至最外层。
将 BootstrapApplicationListener.AncestorInitializer#parent 中保存的 bootstrap ApplicationContext 设置为 原 ApplicationContext 的父级
原 ApplicationContext 新增 ApplicationListener EventPublisher

org.springframework.boot.context.ContextIdApplicationContextInitializer
当前 ApplicationContext 的 id 设置为 application-1

ServerPortInfoApplicationContextInitializer
ApplicationContext的applicationListeners 新增当前类ServerPortInfoApplicationContextInitializer

ServerPortInfoApplicationContextInitializer
ApplicationContext的applicationListeners 新增ConditionEvaluationReportListener

一些ApplicationContextInitializer会在这期间向ApplicationContext中加入一些BeanFactoryPostProcessor

2.6.2 listeners.contextPrepared(context);
发布ApplicationContextInitializedEvent事件

有以下监听器
org.springframework.boot.autoconfigure.BackgroundPreinitializer
org.springframework.boot.context.config.DelegatingApplicationListener

2.6.3 load(context, sources.toArray(new Object[0]));
sources包含了SpringApplication#primarySources,也就是main函数所在的被@SpringBootApplication注解的入口类。

具体执行的操作如下。
2.6.3.1 BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
BeanDefinitionLoader的作用是可以从XML和java注解中加载bean definition。
这行代码里的嵌套调用方法getBeanDefinitionRegistry(context)值得说一下。
2.6.3.1.1 getBeanDefinitionRegistry(context)
这里的context一般都是GenericApplicationContext的子类。
GenericApplicationContext类中有一个成员变量

private final DefaultListableBeanFactory beanFactory;

GenericApplicationContext类和DefaultListableBeanFactory类都实现了BeanDefinitionRegistry接口。GenericApplicationContext类其实是DefaultListableBeanFactory beanFactory的代理,它将所有对
BeanDefinitionRegistry接口的调用都交给了内部成员变量DefaultListableBeanFactory beanFactory去处理。
所以getBeanDefinitionRegistry(context)方法的调用一般得到的还是context自身。
2.6.3.2 loader.load();

当在名为bootstrapConfigurableApplicationContext中时,load时的source是BootstrapImportSelectorConfiguration(在BootstrapApplicationListener中设置的)
而在应用程序的ConfigurableApplicationContext中时,load时的source是程序入口处的XXXpplication.class(应用程序入口main函数中的构造参数引入的)和BootstrapMarkerConfigurationBootstrapApplicationListener#apply方法中引入的,应该是高版本才有这个)。
loader.load();执行过程中,将这个XXXpplication.class这些source封装成BeanDefinition,并将其注册到context中(因为context实现了BeanDefinitionRegistry接口),后续扫描Bean时作为一个扫描入口。

2.6.4 listeners.contextLoaded(context);

SpringApplication.listeners中的ApplicationListener(大多来自1.2节描述的spring.factories加载ApplicationListener)全部加入到
AbstractApplicationContext#applicationListeners中。
发布ApplicationPreparedEvent事件。

以下监听器响应这个事件
org.springframework.boot.context.config.ConfigFileApplicationListener
org.springframework.boot.context.logging.LoggingApplicationListener
org.springframework.boot.autoconfigure.BackgroundPreinitializer
org.springframework.boot.context.config.DelegatingApplicationListener
org.springframework.cloud.context.restart.RestartListener

ConfigFileApplicationListener 增加 PropertySourceOrderingPostProcessor

2.7 refreshContext(context);
这里将会执行很多任务。
首先是调用AbstractApplicationContext.refresh()方法,此方法中一些关键步骤如下。
2.7.1 prepareRefresh();
设置AbstractApplicationContext为active

this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);

2.7.2 prepareBeanFactory(beanFactory);
为beanFactory设置一些值,具体见源码。
2.7.3 invokeBeanFactoryPostProcessors(beanFactory);
这里主要是调用BeanFactoryPostProcessor和他的子接口BeanDefinitionRegistryPostProcessor
它们的实现类有的来自于AbstractApplicationContext#beanFactoryPostProcessors,有的来自于beanfactory中的bean definition。

AbstractApplicationContext#beanFactoryPostProcessors
有以下3个BeanFactoryPostProcessor
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor
org.springframework.boot.context.config.ConfigFileApplicationListener$PropertySourceOrderingPostProcessor

首先调用BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry
先调用排好序的实现了Ordered接口的BeanDefinitionRegistryPostProcessor,然后再调用没有Ordered接口的BeanDefinitionRegistryPostProcessor。接下来介绍部分重要BeanDefinitionRegistryPostProcessor

实现类SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor会向beanfactory加入一个 beandefinition

然后是一个极其重要的实现类,ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry,它来自于 2.5 节介绍的ConfigurableApplicationContext实例化过程。

正如其注释所言,ConfigurationClassPostProcessor会在此时扫描并注册BeanDefinition。

Derive further bean definitions from the configuration classes in the registry.

其中的核心方法为ConfigurationClassParser.parse()

2.7.3.1
由于入口类使用@SpringBootApplication注解,该注解包含了@ComponentScan
ConfigurationClassParser.parse()方法中首先会调用一次
org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass()方法。由于入口类作为一个Bean,且被@ComponentScan注解,所以会自动扫描一下所有的java类来寻找Bean,但此次扫描的basePackages会使用main函数所在类的package。
例如com.demo.SomeTestApplication,则只会扫描com.demo下的bean。
2.7.3.2
ConfigurationClassParser.parse()方法的末尾处this.deferredImportSelectorHandler.process()会进行@Import注解的处理。
由于@SpringBootApplication注解包含了@EnableAutoConfiguration。而@EnableAutoConfiguration又包含了@Import(AutoConfigurationImportSelector.class)
所以会使用AutoConfigurationImportSelector.getCandidateConfigurations()方法对被@EnableAutoConfiguration注解的类进行加载。而获取这些被@EnableAutoConfiguration注解的类的方式是使用1.2.1中提到的SpringFactoriesLoader.loadFactoryNames()方法。即在META-INF/spring.factories文件中,将这些类配置为org.springframework.boot.autoconfigure.EnableAutoConfiguration的对应值,才能被识别。
这些被识别出的class会作为ConfigurationClass放入ConfigurationClassParser.configurationClasses这一map中。
被引入的ConfigurationClass也可以通过被@Import(XXXXImportSelector.class)注解的方式继续import其他ConfigurationClass

2.7.3 续
回到 ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry 方法,
后续通过Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
取出 2.7.3.2 中收集到的ConfigurationClass,使用
ConfigurationClassBeanDefinitionReader.loadBeanDefinitions()方法加载所有ConfigurationClass中配置的Bean Definition。

之后RefreshScope作为BeanDefinitionRegistryPostProcessor的实现类,以及RefreshAutoConfiguration.RefreshScopeBeanDefinitionEnhancer,也会被调用,处理一下refresh相关的类。

然后调用一遍所有BeanDefinitionRegistryPostProcessorpostProcessBeanFactory函数。因为BeanDefinitionRegistryPostProcessor同时也是BeanFactoryPostProcessor的子接口。

最后调用所有BeanFactoryPostProcessor#postProcessBeanFactory。一样,有的来自于AbstractApplicationContext#beanFactoryPostProcessors,有的来自于beanfactory中的bean definition。

先按序调用实现了PriorityOrdered接口的,再按序调用实现了Ordered接口,最后调用其他的。

PropertySourcesPlaceholderConfigurer
org.springframework.context.event.EventListenerMethodProcessor
org.springframework.boot.context.properties.ConfigurationBeanFactoryMetadata
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$PreserveErrorControllerTargetClassPostProcessor

2.7.4 registerBeanPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);

将所有已注册到beanFactory中的所有BeanPostProcessor实现bean,收集排序并注册到beanFactory.beanPostProcessors中,用于后续实例化bean时,对bean进行增强。
按序注册后的BeanPostProcessor

org.springframework.context.support.ApplicationContextAwareProcessor
org.springframework.boot.web.servlet.context.WebApplicationContextServletContextAwareProcessor
org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor
org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker
org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor
AnnotationAwareAspectJAutoProxyCreator
MethodValidationPostProcessor
org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor
org.springframework.boot.web.server.ErrorPageRegistrarBeanPostProcessor
org.springframework.cloud.context.properties.ConfigurationPropertiesBeans
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
org.springframework.context.support.ApplicationListenerDetector

2.7.5 initApplicationEventMulticaster();
AbstractApplicationContext#applicationEventMulticaster设置事件发布器,但此时applicationEventMulticaster内还没有注册任何监听器,没有完整的事件发布功能。

2.7.6 onRefresh();
此函数只做一件重要的事,就是createWebServer();
首先在beanFactory寻找并实例化ServletWebServerFactory对应的bean,默认找到bean是TomcatServletWebServerFactory

注:
spring-boot-dependencies.pom文件中默认定义了依赖tomcat-embed-core.jar包,没有依赖其他的web容器jar包。在 2.7.3 中扫描BeanDefinition时,发现了ServletWebServerFactoryConfiguration配置类。在该配置类中定义了多个WebServer的bean方法,包括Tomcat,Jetty和Undertow。但是因为只有Tomcat.class等tomcat相关类存在,所以只有tomcatServletWebServerFactory()bean method生效,默认实例化的便是TomcatServletWebServerFactory
Tomcat相关流程的源码分析在本文开头的相关文章中介绍,本文不再赘述。

this.webServer = factory.getWebServer(getSelfInitializer());
构造出Tomcat实例,使用Tomcat构造TomcatWebServer实例。TomcatWebServer的构造函数中会调用Tomcat#start。注意这里的Tomcat#start只是走一些Tomcat的启动流程,结束后并不会开始监听端口接受请求。

2.7.7 registerListeners();
AbstractApplicationContext#applicationEventMulticaster注册监听器。监听器来自AbstractApplicationContext#applicationListeners和实现了ApplicationListener的Bean。至此ApplicationContext具有了完整的事件发布功能。

2.7.8 finishBeanFactoryInitialization(beanFactory);
初始化剩余的(non-lazy-init)单例的bean,一般controller啥的就会在这里被实例化

// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

2.7.8 finishRefresh();

2.7.8.1 getLifecycleProcessor().onRefresh();
org.springframework.context.Lifecycle#start
实现了Lifecycle的Bean调用start

2.7.8.1 publishEvent(new ContextRefreshedEvent(this));
发布ContextRefreshedEvent事件
以下Listener会监听此事件

org.springframework.boot.builder.ParentContextApplicationContextInitializer$EventPublisher
org.springframework.boot.context.config.DelegatingApplicationListener
org.springframework.cloud.context.restart.RestartListener
org.springframework.cloud.context.scope.refresh.RefreshScope
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener$ConditionEvaluationReportListener
org.springframework.boot.ClearCachesApplicationListener
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer$SharedMetadataReaderFactoryBean
org.springframework.web.servlet.resource.ResourceUrlProvider

RestartListener会再次发布一遍ApplicationPreparedEvent
RefreshScope初始化RefreshScope bean

2.7.8.2 WebServer webServer = startWebServer();
启动TomcatWebServer,与2.7.4中的启动tomcat时不同的。
此时开始监听服务器端口

2.7.8.3 publishEvent(new ServletWebServerInitializedEvent(webServer, this));
发布ServletWebServerInitializedEvent事件

有以下两个监听器
org.springframework.boot.context.config.DelegatingApplicationListener
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

2.7 续 最后回到SpringApplication.refreshContext(context);方法内部
registerShutdownHook

2.8 listeners.started(context);
发布ApplicationStartedEvent事件

org.springframework.boot.autoconfigure.BackgroundPreinitializer
org.springframework.boot.context.config.DelegatingApplicationListener

2.9 listeners.running(context);
发布ApplicationReadyEvent事件

3 至此,SpringApplication.run()函数全部执行完毕,也就意味着main()函数执行完毕。main线程结束,java虚拟机启动一个DestroyJavaVM线程,并等待其他的非daemon线程执行结束。

Bean 实例化流程

在SpringBoot的启动过程中,大部分流程中随时都可以获取Bean。
通过BeanFactory的各个getBean等方法可以获取Bean。

创建Bean的主要流程位于AbstractAutowireCapableBeanFactory#doCreateBean函数内。

  1. instanceWrapper = createBeanInstance(beanName, mbd, args);
    通过 constructor 反射创建对象。
  1. 之后会使用多个 BeanPostProcessor 处理Bean。但每次都使用一类 BeanPostProcessor 进行处理。以下为所有 BeanPostProcessor
    org.springframework.context.support.ApplicationContextAwareProcessor
    org.springframework.boot.web.servlet.context.WebApplicationContextServletContextAwareProcessor
    org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor
    org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker
    org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor
    org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
    org.springframework.validation.beanvalidation.MethodValidationPostProcessor
    org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor
    org.springframework.boot.web.server.ErrorPageRegistrarBeanPostProcessor
    org.springframework.cloud.context.properties.ConfigurationPropertiesBeans
    org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
    org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
    org.springframework.context.support.ApplicationListenerDetector
  1. applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
    使用MergedBeanDefinitionPostProcessor子类型的BeanPostProcessor进行处理。
    MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition

CommonAnnotationBeanPostProcessor
AutowiredAnnotationBeanPostProcessor
org.springframework.context.support.ApplicationListenerDetector

  1. populateBean(beanName, mbd, instanceWrapper);

3.1 InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation
使用InstantiationAwareBeanPostProcessor子类型的BeanPostProcessor进行处理。

ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor
AnnotationAwareAspectJAutoProxyCreator
CommonAnnotationBeanPostProcessor
AutowiredAnnotationBeanPostProcessor

3.2 InstantiationAwareBeanPostProcessor#postProcessProperties
使用InstantiationAwareBeanPostProcessor子类型的BeanPostProcessor进行处理。

ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor
AnnotationAwareAspectJAutoProxyCreator
CommonAnnotationBeanPostProcessor
AutowiredAnnotationBeanPostProcessor

这期间
CommonAnnotationBeanPostProcessor完成@Resource字段的注入
AutowiredAnnotationBeanPostProcessor完成@Value@Autowired字段的注入

  1. exposedObject = initializeBean(beanName, exposedObject, mbd);
    4.1 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    调用所有BeanPostProcessor#postProcessBeforeInitialization

其中CommonAnnotationBeanPostProcessor会调用@PostConstruct注解的函数。

4.2 invokeInitMethods(beanName, wrappedBean, mbd);
调用 org.springframework.beans.factory.InitializingBean#afterPropertiesSet

4.3 applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
调用所有BeanPostProcessor#postProcessAfterInitialization

注意事项

  1. 响应ApplicationEvent事件

接受各种Spring的ApplicationEvent接收器时,如ContextRefreshedEvent等事件。需要注意幂等性。因为系统启动过程中一个事件可能会被接受多次。
比如

  1. bootstrap启动过程中,接受一次事件
  2. 回到原应用程序,原应用程序的启动过程中,接受一次事件
  3. 原应用程序抛出时间时,还会触发parent的监听器(也就是bootstrap的监听器),接受一次事件
  4. 一些特殊类,比如RestartListener还会再次抛出ApplicationPreparedEvent事件
  1. SpringApplicationEvent各个事件的抛出位置
事件类型(SpringApplicationEvent的子类) SpringApplicationRunListener接口的调用方法 本文的章节号
ApplicationStartingEvent starting() 2.2
ApplicationEnvironmentPreparedEvent environmentPrepared() 2.3.3
ApplicationContextInitializedEvent contextPrepared() 2.6.2
ApplicationPreparedEvent contextLoaded() 2.6.4
ApplicationStartedEvent started() 2.8
ApplicationReadyEvent running() 2.9

排查问题方式

  1. controller中获取ApplicationContext
    1.1 调用栈底部的函数获取
    在controller的请求上打断点,调用栈底部找到StandardWrapperValve.invoke函数,方法内的局部变量servlet中包含了webApplicationContext字段,里边有beanfactory和environment.propertisource等

1.2 借助ThreadLocal获取

RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes) {
    ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
    ServletContext servletContext = servletRequestAttributes.getRequest().getServletContext();
    WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容