本篇文章将从Spring Boot的main函数入口,一步一步带领大家阅读spring boot的源代码,并且会详细解释spring boot各个类和方法上注释的含义,帮助阅读英文有困难的同学更好的理解spring boot的原理,话不多说,直接开始。
程序入口
通常在一个spring boot的应用中,会看到下面一段代码作为应用的入口。
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
那么这段代码究竟做了什么呢,让我们深入来分析它背后的原理。当我们点击run来查看源代码时,会看到下面这段代码,这段注释说明这是一个助手方法,可以通过指定一个primarySource
的source源来启动,这个primarySource
其实就是我们的启动类MyApplication。
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified source using default settings.
* @param primarySource the primary source to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
接着,我们继续进入run方法,你会看到另外一个helper方法,这个helper方法首先初始化一个SpringApplication,然后再一次执行SpringApplication实例的run方法。
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// 构造SpringApplication对象
return new SpringApplication(primarySources).run(args);
}
1、构造SpringApplication对象
我们将一行一行逐步讲解初始化SpringApplication
的逻辑
首先将启动类放入primarySources
// 将启动类放入primarySources
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
1、推测Web应用类型
- 如果项目依赖中存在org.springframework.web.reactive.DispatcherHandler,并且不存在org.springframework.web.servlet.DispatcherServlet,那么应用类型为WebApplicationType.REACTIVE
- 如果项目依赖中不存在org.springframework.web.reactive.DispatcherHandler,也不存在org.springframework.web.servlet.DispatcherServlet,那么应用类型为WebApplicationType.NONE
- 否则,应用类型为WebApplicationType.SERVLET
// 1. 推测web应用类型(NONE、REACTIVE、SERVLET)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
进入deduceFromClasspath 方法:
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
2、获取BootstrapRegistryInitializer对象
- 从"META-INF/spring.factories"中读取key为BootstrapRegistryInitializer类型的扩展点,并实例化出对应扩展点对象
- BootstrapRegistryInitializer的作用是可以初始化BootstrapRegistry
- 上面的DefaultBootstrapContext对象就是一个BootstrapRegistry,可以用来注册一些对象,这些对象可以用在从SpringBoot启动到Spring容器初始化完成的过程中
- 我的理解:没有Spring容器之前就利用BootstrapRegistry来共享一些对象,有了Spring容器之后就利用Spring容器来共享一些对象
// 2. 从spring.factories中获取BootstrapRegistryInitializer对象
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
进入getSpringFactoriesInstances
方法:
有人可能会问,BootstrapRegistryInitializer
是干什么的呀,我们为什么要加载BootstrapRegistryInitializer
呢,我们从Java Doc里就可以明白这个接口是通过Initialize
的方法来加载BootstrapRegistry
的。
/**
* Callback interface that can be used to initialize a {@link BootstrapRegistry} before it
* is used.
*
* @author Phillip Webb
* @since 2.4.5
* @see SpringApplication#addBootstrapRegistryInitializer(BootstrapRegistryInitializer)
* @see BootstrapRegistry
*/
@FunctionalInterface
public interface BootstrapRegistryInitializer {
/**
* Initialize the given {@link BootstrapRegistry} with any required registrations.
* @param registry the registry to initialize
*/
void initialize(BootstrapRegistry registry);
}
可能又有人问,什么是 BootstrapRegistry
,我们继续看Java Doc,继承这个注册接口的类会在ApplicationContext
准备好之前可用,并且接口上有一个register
的方法和一个addCloseListener
- register: 以class为key来注册一些对象的实例来使用
- addCloseListener: 在BootstrapContext关闭和ApplicationContext准备好之前调用该Listener
那么我们可以简单理解为这个BootstrapRegistry
是为了让spring boot在 ApplicationContext
准备好之前能有机会来注册一些类,并且通过触发ApplicationContext ready
的事件来处理一些逻辑
3、获取ApplicationContextInitializer对象
- 从"META-INF/spring.factories"中读取key为ApplicationContextInitializer类型的扩展点,并实例化出对应扩展点对象
- 顾名思义,ApplicationContextInitializer是用来初始化Spring容器ApplicationContext对象的,比如可以利用ApplicationContextInitializer来向Spring容器中添加ApplicationListener
// 3. 从spring.factories中获取ApplicationContextInitializer对象
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
4、获取ApplicationListener对象
- 从"META-INF/spring.factories"中读取key为ApplicationListener类型的扩展点,并实例化出对应扩展点对象
- ApplicationListener是Spring中的监听器,并不是SpringBoot中的新概念,不多解释了
// 4. 从spring.factories中获取ApplicationListener对象
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
5、推测出Main类(main()方法所在的类)
- 没什么具体的作用,逻辑是根据当前线程的调用栈来判断main()方法在哪个类,哪个类就是Main类
// 5. 推测出Main类(main()方法所在的类)
this.mainApplicationClass = deduceMainApplicationClass();
2、启动SpringBoot:run(agrs)
现在开始拆解启动SpringApplication的过程。
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
// 1、创建引导启动器,类似一个ApplicationContext,可以往里面添加一些对象
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
// 2、从spring.factories中获取SpringApplicationRunListener对象
// 默认会拿到一个EventPublishingRunListener,它会启动过程的各个阶段发布对应的ApplicationEvent事件
SpringApplicationRunListeners listeners = getRunListeners(args);
// 3、发布ApplicationStartingEvent
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 4、将run()的参数封装为DefaultApplicationArguments对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 5、准备Environment
// 包括操作系统,JVM、ServletContext、properties、yaml等等配置
// 会发布一个ApplicationEnvironmentPreparedEvent
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 默认spring.beaninfo.ignore=true,表示不需要jdk缓存beanInfo信息,Spring自己会缓存
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
// 6、根据应用类型创建Spring容器
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 7、利用ApplicationContextInitializer初始化Spring容器
// 8、发布ApplicationContextInitializedEvent
// 9、关闭DefaultBootstrapContext
// 10、注册primarySources类,就是run方法存入进来的配置类
// 11、发布ApplicationPreparedEvent事件
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 12、刷新Spring容器,会解析配置类、扫描、启动WebServer
refreshContext(context);
// 空方法
afterRefresh(context, applicationArguments);
// 启动时间
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
// 13、发布ApplicationStartedEvent事件,表示Spring容器已经启动
listeners.started(context, timeTakenToStartup);
// 14、从Spring容器中获取ApplicationRunner和CommandLineRunner,并执行其run()
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// 15、发布ApplicationFailedEvent事件
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
// 16、发布ApplicationReadyEvent事件,表示Spring容器已经准备好了
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
1、创建DefaultBootstrapContext
对象
- 首先创建一个default的bootstrapContext, 然后通过之前在初始化SpringApplication时找到的 bootstrapRegistryInitializers, 来初始化一个bootstrapContext
- BootstrapRegistryInitializer是一个接口,Spring Boot内并没有实现,需要用户根据自己的业务需求自己创建
private DefaultBootstrapContext createBootstrapContext() {
// 创建一个default的bootstrapContext
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
// 利用bootstrapRegistryInitializers初始化DefaultBootstrapContext
this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
return bootstrapContext;
}
2、设置java.awt.headless
根据系统变量java.awt.headless的值来设置Java headless mode,缺省值是true,因为大多数的Spring Boot项目都不是交互式应用,不需要显示设备,键盘或者鼠标等,所以默认值是headless mode
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
3、获得SpringApplicationRunListener
通过getSpringFactoriesInstances方法获得并初始化所有在META/spring.factories里的SpringApplicationRunListener的子类。
默认会拿到一个EventPublishingRunListener,它会启动过程的各个阶段发布对应的ApplicationEvent事件
private SpringApplicationRunListeners getRunListeners(String[] args) {
// 任何实现SpringApplicationRunListener.class的子类的构造函数必须有这2个类型的入参
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
// 找到所有的SpringApplicationRunListener.class的子类并实例化
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
this.applicationStartup);
}
这里有个细节,就是这个继承SpringApplicationRunListener
的子类必须有一个构造方法,它的入参是SpringApplication
和String[].class
,不然不会被实例化,而Spring的默认实现中只有一个子类EventPublishingRunListener
,而这个默认的实现是用来广播在context准备好之前的事件的,例如ApplicationStartingEvent
, ApplicationEnvironmentPreparedEvent
等
this.applicationStartup
可以用来收集一些启动时的数据,这样一来如果我们的Spring Boot应用在启动时遇到了问题,就会比较容易定位问题。
4、发布ApplicationStartingEvent事件
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
(step) -> {
if (mainApplicationClass != null) {
step.tag("mainApplicationClass", mainApplicationClass.getName());
}
});
}
这里listener.starting(bootstrapContext)
中的Listener就是上面提到的EventPublishingRunListener
,下面的代码就是该类上的starting
方法 ,可以很清楚的看到这里广播了一个ApplicationStartingEvent
事件,广播事件使用了观察者的设计模式,这里就不展开了,有兴趣的读者可自行查看源码。
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
// 这里的广播机制使用了典型的观察者模式
this.initialMulticaster
.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}
5、封装命令行参数ApplicationArguments
// 将run()的参数封装为DefaultApplicationArguments对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
6、创建Environment对象
Environment对象表示环境变量,该对象内部主要包含了:
- 当前操作系统的环境变量
- JVM的一些配置信息
- -D方式所配置的JVM环境变量
整个准备Environment的步骤相对复杂,现在对其逐一拆解
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
// 创建ApplicationServletEnvironment,里面添加了四个PropertySource
// 1. StubPropertySource {name='servletConfigInitParams'}
// 2. StubPropertySource {name='servletContextInitParams'}
// 3. PropertiesPropertySource {name='systemProperties'}
// 4. SystemEnvironmentPropertySource {name='systemEnvironment'}
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 添加SimpleCommandLinePropertySource {name='commandLineArgs'},放在首位
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 把所有的PropertySources封装为一个ConfigurationPropertySourcesPropertySource
// 然后添加到environment中,放在首位
ConfigurationPropertySources.attach(environment);
// 发布ApplicationEnvironmentPreparedEvent事件,表示环境已经准备好了
// 默认EnvironmentPostProcessorApplicationListener会处理这个事件,会从spring.factories中拿出EnvironmentPostProcessor进一步处理Environment
listeners.environmentPrepared(bootstrapContext, environment);
// 最后,把defaultProperties移到最后
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
"Environment prefix cannot be set via properties.");
// 绑定environment
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
// 转换environment
environment = convertEnvironment(environment);
}
// 重新添加ConfigurationPropertySource支持
ConfigurationPropertySources.attach(environment);
return environment;
}
1、根据webApplicationType创建变量对象
此时已经读取到了:java环境变量和系统环境变量
ConfigurableEnvironment environment = getOrCreateEnvironment();
2、配置环境变量
这里使用的缺省的configureEnvironment
方法,只是简单的将我们Spring Boot的启动参数放进了Environment的propertySources
中。因为这是一个preotected
方法,所以我们也可以定制我们自己的configureEnvironment
方法来达到完全控制Environment
初始化的目的。
configureEnvironment(environment, applicationArguments.getSourceArgs());
/**
* 这是个模板方法通过configurePropertySources和configureProfiles来完成配置
* 我们也可以覆盖整个configureEnvironment方法来完全控制Environment的初始化
* 或者只覆盖其中configurePropertySources和configureProfiles方法来部分定制化我们的配置
*/
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
// 添加一些类型转化器,比如把properties文件中的字符串转化成各种类型
environment.setConversionService(new ApplicationConversionService());
}
// 添加SimpleCommandLinePropertySource {name='commandLineArgs'},放在首位
configurePropertySources(environment, args);
// 空方法
configureProfiles(environment, args);
}
3、将现有信息设置为@configurationProperties的数据源,并且放在第一位
/**
* 添加一个ConfigurationPropertySource的支持,任何被ConfigurationPropertySource管理的Environment
* 都将支持ConfigurationPropertyName的格式,并且能够动态的跟踪propertySource内变量的增加或减少
*/
public static void attach(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
PropertySource<?> attached = getAttached(sources);
if (attached == null || !isUsingSources(attached, sources)) {
attached = new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
new SpringConfigurationPropertySources(sources));
}
sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
sources.addFirst(attached);
}
这里将Environment添加到一个ConfigurationPropertySource中去,这样做的好处有:
- 可以支持ConfigurationPropertyName的属性名格式,比如属性名中出现server.hosts[0].name和 log[org.springboot].level这样的格式将能够解析
- 可以跟踪增加或者删除propertySources
4、发布ApplicationEnvironmentPreparedEvent
listeners.environmentPrepared(bootstrapContext, environment);
void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
doWithListeners("spring.boot.application.environment-prepared",
(listener) -> listener.environmentPrepared(bootstrapContext, environment));
}
5、绑定Environment
这里会重新创建一个Binder的实例,通过Binder将环境变量与SpringApplication绑定。
protected void bindToSpringApplication(ConfigurableEnvironment environment) {
try {
// 比如spring.main.allowBeanDefinitionOverriding=true
Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
}
catch (Exception ex) {
throw new IllegalStateException("Cannot bind to SpringApplication", ex);
}
}
6、转换Environment
如果这不是一个定制的Environment
,那么将对之前的Environment
进行转换,生成StandardEnvironment
。
if (!this.isCustomEnvironment) {
environment = convertEnvironment(environment);
}
7、设置spring.beaninfo.ignore
private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
}
}
此段较为简单,只是设置spring.beaninfo.ignore的值。缺省为true的意思就是使用spring内置的BeanInfo,而BeanInfo又是什么,简单来说就是用来将各种前段返回的数据直接映射到JavaBean上。
8、打印Banner
我们知道每个Spring Boot在启动的时候都会打印一个经典的图案,就是这里实现的,当然你可以换成自己的图案,支持文本格式或者图片格式,可以通过把banner.txt放到指定目录或者自定义自己的banner目录,这里就不展开了,有兴趣的读者可以自行研究。
9、根据应用类型创建Spring容器,实例化Spring上下文
默认是AnnotationConfigApplicationContext
context = createApplicationContext();
10、准备上下文
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 将前面生成的Environment设置到Spring容器中
context.setEnvironment(environment);
// 将设置在SpringApplication上的beanNameGenerator、resourceLoader设置到Spring容器中
postProcessApplicationContext(context);
// 利用ApplicationContextInitializer初始化Spring容器
applyInitializers(context);
// 发布ApplicationContextInitializedEvent事件,表示Spring容器初始化完成
listeners.contextPrepared(context);
// Spring容器初始化好了,就关闭DefaultBootstrapContext
bootstrapContext.close(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 注册一些单例Bean
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
// 设置allowCircularReferences和allowBeanDefinitionOverriding给Spring容器
if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
// 拿到启动配置类(run方法传进来的类)
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 将启动配置类解析为BeanDefinition注册到Spring容器中
load(context, sources.toArray(new Object[0]));
// 发布ApplicationPreparedEvent事件,表示Spring容器已经准备好
listeners.contextLoaded(context);
}
整个准备上下文的步骤相对复杂,现在对其逐一拆解。
1、将当前的环境信息设置到context
context.setEnvironment(environment)
2、将设置在SpringApplication上的beanNameGenerator、resourceLoader设置到Spring容器中
postProcessApplicationContext(context);
3、调用ApplicationContextInitializer
applyInitializers(context)
4、发布ApplicationContextInitializedEvent
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
5、关闭DefaultBootstrapContext
bootstrapContext.close(context);
6、打印启动信息和profile.active信息
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
7、将applicationArguments、printedBanner注册成bean
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
8、设置不允许同名的bean
// 设置allowCircularReferences和allowBeanDefinitionOverriding给Spring容器
if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
}
9、设置是否懒加载bean
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
10、根据启动类加载所有的bean
这是容器加载非常关键的一步, 因为要根据启动类加载所有bean , 在这里启动类就相当于配置文件xml ,如果你之前看过AnnotationConfigApplicationContext, 它也会读取配置类, 一个意思。
// 将启动配置类解析为BeanDefinition注册到Spring容器中
load(context, sources.toArray(new Object[0]));
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
loader.load();
}
11、刷新容器–加载ioc容器
afterRefresh(context, applicationArguments);
12、afterRefresh --待扩展
afterRefresh(context, applicationArguments);
13、触发SpringApplicationRunListener的started()
listeners.started(context);
发布ApplicationStartedEvent事件和AvailabilityChangeEvent事件,AvailabilityChangeEvent事件表示状态变更状态,变更后的状态为LivenessState.CORRECT。
LivenessState枚举有两个值:
-
- CORRECT:表示当前应用正常运行中
-
- BROKEN:表示当前应用还在运行,但是内部出现问题,暂时还没发现哪里用到了
14、调用ApplicationRunner和CommandLineRunner
- 获取Spring容器中的ApplicationRunner类型的Bean
- 获取Spring容器中的CommandLineRunner类型的Bean
- 执行它们的run()
15、触发SpringApplicationRunListener的ready()
发布ApplicationReadyEvent事件和AvailabilityChangeEvent事件,AvailabilityChangeEvent事件表示状态变更状态,变更后的状态为ReadinessState.ACCEPTING_TRAFFIC
ReadinessState枚举有两个值:
- ACCEPTING_TRAFFIC:表示当前应用准备接收请求
- REFUSING_TRAFFIC:表示当前应用拒绝接收请求,比如Tomcat关闭时,就会发布AvailabilityChangeEvent事件,并且状态为REFUSING_TRAFFIC
16、上述过程抛异常了就触发SpringApplicationRunListener的failed()
发布ApplicationFailedEvent事件
3、配置文件解析
4、SpringBoot完整的配置优先级
优先级由高到低:
- Default properties (specified by setting SpringApplication.setDefaultProperties).
- @PropertySource
- annotations on your @Configuration classes. Please note that such property sources are not added to the Environment until the application context is being refreshed. This is too late to configure certain properties such as logging.* and spring.main.* which are read before refresh begins.
- Config data (such as application.properties files).
- A RandomValuePropertySource that has properties only in random.*.
- OS environment variables.
- Java System properties (System.getProperties()).
- JNDI attributes from java:comp/env. 不管它
- ServletContext init parameters.
- ServletConfig init parameters.
- Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property).
- Command line arguments.
- properties attribute on your tests. Available on @SpringBootTest and the test annotations for testing a particular slice of your application.
- @TestPropertySource annotations on your tests.
- Devtools global settings properties in the $HOME/.config/spring-boot directory when devtools is active.