前言
Spring Boot 版本 2.1.7.RELEASE
spring-cloud-dependencies 版本 Greenwich.SR5
本文大致跟踪了一遍Spring Boot的启动流程。
请边debug源代码边看本文,因为很多变量细节在debug时才能清晰的观察到,而本文是无法全部涵盖所有细节的。
文中如有错误遗漏,请留言指正。
相关文章
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
的成员变量initializers
和listeners
赋值。二者均使用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
servletContextInitParams
为StubPropertySource
,这是一个没有任何内容的占位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
和一个名为bootstrap
的ConfigurableApplicationContext
并run()
起来。
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
字段还没有设置为bootstrap
,run()
执行完成后才会设置,而且在程序入口的那个ConfigurableApplicationContext
的prepareContext
阶段中,这个名为"bootstrap"
的ConfigurableApplicationContext
会作为应用的ConfigurableApplicationContext
的父级)
这个名为"bootstrap"
的ConfigurableApplicationContext
会有一些特殊配置,比如。再次执行run()
的过程中还会触发BootstrapApplicationListener
,不过当发现已经引入名为bootstrap
的PropertySource
时,就不再执行后续代码了,避免了陷入不断触发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()
返回 AnnotationConfigApplicationContext
,AnnotationConfigApplicationContext
的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的末尾,最终依次新增PropertySourceBootstrapConfiguration
和EnvironmentDecryptApplicationInitializer
。再单独新增一个DelegatingEnvironmentDecryptApplicationInitializer
。
2.3.3.2 ConfigFileApplicationListener
原SpringApplication
的ApplicationEnvironmentPreparedEvent
事件的监听器继续执行。
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
,分别是PropertiesPropertySourceLoader
和 YamlPropertySourceLoader
。
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"
,使用PropertiesPropertySourceLoader
和 YamlPropertySourceLoader
加载配置文件内容,生成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();
当在名为bootstrap
的ConfigurableApplicationContext
中时,load时的source是BootstrapImportSelectorConfiguration
(在BootstrapApplicationListener
中设置的)
而在应用程序的ConfigurableApplicationContext
中时,load时的source是程序入口处的XXXpplication.class
(应用程序入口main
函数中的构造参数引入的)和BootstrapMarkerConfiguration
(BootstrapApplicationListener#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相关的类。
然后调用一遍所有BeanDefinitionRegistryPostProcessor
的postProcessBeanFactory
函数。因为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
函数内。
-
instanceWrapper = createBeanInstance(beanName, mbd, args);
通过 constructor 反射创建对象。
- 之后会使用多个
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
-
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
使用MergedBeanDefinitionPostProcessor
子类型的BeanPostProcessor
进行处理。
MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition
CommonAnnotationBeanPostProcessor
AutowiredAnnotationBeanPostProcessor
org.springframework.context.support.ApplicationListenerDetector
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
字段的注入
-
exposedObject = initializeBean(beanName, exposedObject, mbd);
4.1wrappedBean = 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
注意事项
- 响应
ApplicationEvent
事件
接受各种Spring的ApplicationEvent
接收器时,如ContextRefreshedEvent
等事件。需要注意幂等性。因为系统启动过程中一个事件可能会被接受多次。
比如
- bootstrap启动过程中,接受一次事件
- 回到原应用程序,原应用程序的启动过程中,接受一次事件
- 原应用程序抛出时间时,还会触发parent的监听器(也就是bootstrap的监听器),接受一次事件
- 一些特殊类,比如
RestartListener
还会再次抛出ApplicationPreparedEvent
事件
-
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 |
排查问题方式
-
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);
}