组里的实习生妹妹讲了一次Springboot的启动,
讲着讲着就讲到Spring的bean的生命周期去了,
我心想坏了,
这妮子估计把Springboot和Spring的相关逻辑给混淆了,
这必须得给她治一治。
前言
本文会对Springboot启动流程进行详细分析。但是请注意,Springboot启动流程是Springboot的逻辑,请千万不要将Springboot启动流程相关逻辑与Spring的相关逻辑混在一起,比如把Spring的bean生命周期的逻辑混在Springboot启动流程中,那么整个体系就复杂且混乱了。
所以本文仅重点关注Springboot启动流程,涉及Spring的部分,会略作说明并跳过。
整体的一个结构图如下。
Springboot版本:2.4.1
正文
一. Springboot启动流程图及说明
如下是Springboot的一个启动流程图。
在SpringApplication完成初始化后,就会调用SpringApplication对象的run() 方法,该方法就是Springboot启动的入口,也对应着全流程图中的开始。下面给出SpringApplication对象的run() 方法说明,如下所示。
public ConfigurableApplicationContext run(String... args) { // 创建StopWatch,用于统计Springboot启动的耗时 StopWatch stopWatch = new StopWatch(); // 开始计时 stopWatch.start(); DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null; configureHeadlessProperty(); // 获取运行时监听器 SpringApplicationRunListeners listeners = getRunListeners(args); // 调用运行时监听器的starting()方法 // 该方法需要在Springboot一启动时就调用,用于特别早期的初始化 listeners.starting(bootstrapContext, this.mainApplicationClass); try { // 获取args参数对象 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 读取Springboot配置文件并创建Environment对象 // 这里创建的Environment对象实际为ConfigurableEnvironment ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); configureIgnoreBeanInfo(environment); // 打印Banner图标 Banner printedBanner = printBanner(environment); // 创建ApplicationContext应用行下文,即创建容器 context = createApplicationContext(); context.setApplicationStartup(this.applicationStartup); // 准备容器 prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); // 初始化容器 refreshContext(context); afterRefresh(context, applicationArguments); // 停止计时 stopWatch.stop(); if (this.logStartupInfo) { // 打印启动耗时等信息 new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } // 调用运行时监听器的started()方法 // 该方法需要在应用程序启动后,CommandLineRunners和ApplicationRunners被调用前执行 listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } try { // 调用运行时监听器的running()方法 // 该方法需要在SpringApplication的run()方法执行完之前被调用 listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, null); throw new IllegalStateException(ex); } return context;}
二. SpringApplication的初始化
通常,Springboot应用程序的启动类定义如下。@SpringBootApplicationpublic class LearnStartApplication { public static void main(String[] args) { SpringApplication.run(LearnStartApplication.class, args); }}
从SpringApplication的静态run() 方法一路跟进,会发现如下的实现。
public static ConfigurableApplicationContext run(Class primarySource, String... args) { return run(new Class[] { primarySource }, args);}public static ConfigurableApplicationContext run(Class[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args);}
也就是Springboot启动时会先创建SpringApplication,然后再通过SpringApplication的run() 方法完成启动。所以下面分析一下SpringApplication的初始化逻辑,其构造方法如下所示。public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, 'PrimarySources must not be null'); // 设置源 // 通常Springboot的启动类就是源 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 推断并设置WEB应用程序类型 // 根据classpath下的类来推断 this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 加载并设置Bootstrapper this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class)); // 加载并设置初始化器 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 加载并设置应用事件监听器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 推断并设置应用程序主类的Class对象 this.mainApplicationClass = deduceMainApplicationClass();}
梳理一下在SpringApplication的构造方法中,做了如下事情。
设置源。通常,Springboot中的源就是Springboot的启动类;
设置WEB应用程序类型。通过判断classpath下是否存在某些类,来推断当前WEB应用程序的类型;
加载并设置Bootstrapper,ApplicationContextInitializer和ApplicationListener。借助SpringFactoriesLoader基于SPI机制完成Bootstrapper,ApplicationContextInitializer和ApplicationListener的加载,然后设置到SpringApplication中;
设置应用程序主类的Class对象。
下面对上述事情进行分析。
1. 设置源
这里的源,也就是Spring容器启动时依赖的初始配置类,在Springboot中,初始配置类通常为启动类。下面可以通过调试看一下primarySources字段的值,如下所示。
可见源就是Springboot的启动类的Class对象。
2. 设置WEB应用程序类型
WebApplicationType#deduceFromClasspath方法如下所示。
private static final String[] SERVLET_INDICATOR_CLASSES = { 'javax.servlet.Servlet', 'org.springframework.web.context.ConfigurableWebApplicationContext' };private static final String WEBMVC_INDICATOR_CLASS = 'org.springframework.web.servlet.DispatcherServlet';private static final String WEBFLUX_INDICATOR_CLASS = 'org.springframework.web.reactive.DispatcherHandler';private static final String JERSEY_INDICATOR_CLASS = 'org.glassfish.jersey.servlet.ServletContainer';private static final String SERVLET_APPLICATION_CONTEXT_CLASS = 'org.springframework.web.context.WebApplicationContext';private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = 'org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext';static WebApplicationType deduceFromClasspath() { if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { // classpath下存在DispatcherHandler,但不存在DispatcherServlet和ServletContainer // 则WEN应用程序类型推断为REACTIVE,即响应式WEB应用程序 return WebApplicationType.REACTIVE; } for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null)) { // 非WEB应用程序 return WebApplicationType.NONE; } } // 基于Servlet的WEB应用程序 return WebApplicationType.SERVLET;}
在WebApplicationType中预定义了若干种用于判断的类的全限定名,然后在deduceFromClasspath() 方法中使用ClassUtils来判断预定义的类是否存在,通过这样的方式最终可以推断出当前WEB应用程序类型。在示例工程中,如果只引入spring-boot-starter包,那么推断出来的WebApplicationType为NONE,如下所示。
如果再引入spring-boot-starter-web包,则推断出来的WebApplicationType为SERVLET,如下所示。
3. 加载并设置Bootstrapper,ApplicationContextInitializer和ApplicationListener
这里主要分析一下是如何加载Bootstrapper,
ApplicationContextInitializer和ApplicationListener的。它们的加载均使用了
getSpringFactoriesInstances() 方法,下面看一下实现。private Collection getSpringFactoriesInstances(Class type) { return getSpringFactoriesInstances(type, new Class[] {});}private Collection getSpringFactoriesInstances(Class type, Class[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // 通过SpringFactoriesLoader扫描classpath所有jar包的META-INF目录下的spring.factories文件 // 将type全限定名对应的全限定名的集合获取到 Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 实例化 List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances;}
主要就是基于SpringFactoriesLoader完成加载,加载机制和Springboot中的自动装配是一样,唯一的区别就是自动装配中在spring.factories文件中是根据@EnableAutoConfiguration的全限定名作为key去获取全限定名集合,而在这里是根据Bootstrapper,
ApplicationContextInitializer和ApplicationListener的全限定名作为key去获取全限定名集合,以spring-boot-autoconfigure包中的spring.factories文件为例,说明如下。
org.springframework.context.ApplicationContextInitializer=\org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListenerorg.springframework.context.ApplicationListener=\org.springframework.boot.autoconfigure.BackgroundPreinitializer
4. 设置应用程序主类的Class对象
获取应用程序主类的Class对象的SpringApplication#
deduceMainApplicationClass方法如下所示。private Class deduceMainApplicationClass() { try { // 通过RuntimeException获取堆栈 StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { // 判断堆栈元素的发生方法名是否为main if ('main'.equals(stackTraceElement.getMethodName())) { // 通过反射获取到main方法所在类的Class对象 return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { } return null;}
获取应用程序主类的Class对象是通过堆栈实现的,下面给出调试截图。
三. Springboot事件机制
在Springboot启动的一开始,有一步逻辑是获取运行时监听器,最终会获取到一个
SpringApplicationRunListeners对象,下面看一下获取运行时监听器的getRunListeners() 方法的实现。
private SpringApplicationRunListeners getRunListeners(String[] args) { Class[] types = new Class[] { SpringApplication.class, String[].class }; // 先基于SpringFactoriesLoader的SPI机制获取SpringApplicationRunListener的实现类集合 // 然后创建SpringApplicationRunListeners对象 return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup);}SpringApplicationRunListeners(Log log, Collection listeners, ApplicationStartup applicationStartup) { // SpringApplicationRunListeners的构造方法中只是进行简单赋值 this.log = log; this.listeners = new ArrayList<>(listeners); this.applicationStartup = applicationStartup;}
在getRunListeners() 方法中会先基于SpringFactoriesLoader的SPI机制将
SpringApplicationRunListener接口的实现类获取出来,在spring-boot包中提供了一个
SpringApplicationRunListener接口的实现类,为
EventPublishingRunListener,这也是Springboot提供的唯一一个内置运行时监听器,所以通过getRunListeners() 方法获取到的
SpringApplicationRunListeners对象中持有一个
SpringApplicationRunListener的集合,这个集合中默认情况下一定会包含一个
EventPublishingRunListener的对象。
下面再以
SpringApplicationRunListeners的starting() 方法为例,分析一下
SpringApplicationRunListeners是如何工作的。void starting(ConfigurableBootstrapContext bootstrapContext, Class mainApplicationClass) { doWithListeners('spring.boot.application.starting', (listener) -> listener.starting(bootstrapContext), (step) -> { if (mainApplicationClass != null) { step.tag('mainApplicationClass', mainApplicationClass.getName()); } });}private void doWithListeners(String stepName, Consumer listenerAction, Consumer stepAction) { StartupStep step = this.applicationStartup.start(stepName); // 集合中每个运行时监听器都会执行listenerAction函数 this.listeners.forEach(listenerAction); if (stepAction != null) { stepAction.accept(step); } step.end();}
结合
SpringApplicationRunListeners的starting() 和doWithListeners() 方法,可知
SpringApplicationRunListeners会将starting() 方法的调用传递给其持有的每个运行时监听器,所以
SpringApplicationRunListeners是组合模式的一个应用。
那么Springboot中的事件机制按理应该由Springboot提供的唯一一个运行时监听器
EventPublishingRunListener实现。下面分析
EventPublishingRunListener的逻辑,还是以
EventPublishingRunListener的starting() 方法为例,进行说明。
public void starting(ConfigurableBootstrapContext bootstrapContext) { // 先创建一个ApplicationStartingEvent事件对象 // 然后调用SimpleApplicationEventMulticaster来发布事件对象 this.initialMulticaster .multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));}
EventPublishingRunListener的starting() 方法中会先创建ApplicationStartingEvent事件对象,然后通过
EventPublishingRunListener持有的一个
SimpleApplicationEventMulticaster对象来发布事件。
那么下面继续分析
SimpleApplicationEventMulticaster怎么发布事件,发布给谁,
SimpleApplicationEventMulticaster的multicastEvent() 方法如下所示。public void multicastEvent(ApplicationEvent event) { multicastEvent(event, resolveDefaultEventType(event));}public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); // 调用getApplicationListeners()方法将所有适合接收当前事件的ApplicationListener获取出来 // 然后基于异步或者同步的方式向符合条件的ApplicationListener发布事件 for (ApplicationListener listener : getApplicationListeners(event, type)) { if (executor != null) { // 异步发布 executor.execute(() -> invokeListener(listener, event)); } else { // 同步发布 invokeListener(listener, event); } }}
SimpleApplicationEventMulticaster的multicastEvent() 方法中会先将初始化SpringApplication时加载的ApplicationListener获取到,然后遍历其中适合接收当前事件的ApplicationListener,然后异步或者同步的向ApplicationListener发布事件,继续看invokeListener() 方法,如下所示。
protected void invokeListener(ApplicationListener listener, ApplicationEvent event) { ErrorHandler errorHandler = getErrorHandler(); // 实际调用doInvokeListener()方法来向ApplicationListener发布事件 if (errorHandler != null) { try { doInvokeListener(listener, event); } catch (Throwable err) { errorHandler.handleError(err); } } else { doInvokeListener(listener, event); }}private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { try { // ApplicationListener接口的实现类都会实现onApplicationEvent()方法 // 在onApplicationEvent()方法中会处理当前接收到的事件 listener.onApplicationEvent(event); } catch (ClassCastException ex) { String msg = ex.getMessage(); if (msg == null || matchesClassCastMessage(msg, event.getClass())) { Log logger = LogFactory.getLog(getClass()); if (logger.isTraceEnabled()) { logger.trace('Non-matching event type for listener: ' + listener, ex); } } else { throw ex; } }}
SimpleApplicationEventMulticaster的invokeListener() 方法中实际会调用到doInvokeListener() 方法,在doInvokeListener() 方法中会调用ApplicationListener的onApplicationEvent() 方法,所以在这里就调用到了ApplicationListener实际处理事件的逻辑。
现在对Springboot中的事件监听机制进行小结。
SpringApplication初始化时会加载所有的ApplicationListener;
在Springboot启动的一开始,会调用到SpringApplication#getRunListeners方法创建一个SpringApplicationRunListeners对象;
SpringApplicationRunListeners是组合模式的应用,其持有一个SpringApplicationRunListener的集合,集合中默认会存在一个Springboot提供的SpringApplicationRunListener的实现类EventPublishingRunListener,所有对SpringApplicationRunListeners的调用请求都会被传递给集合中的每一个SpringApplicationRunListener;
EventPublishingRunListener中持有一个事件发布器SimpleApplicationEventMulticaster,在EventPublishingRunListener的构造函数中,会将SimpleApplicationEventMulticaster创建出来并将SpringApplication中的所有ApplicationListener设置给SimpleApplicationEventMulticaster。当EventPublishingRunListener的starting(),environmentPrepared() 等方法被调用时,EventPublishingRunListener会创建对应的事件(ApplicationStartingEvent,ApplicationEnvironmentPreparedEvent)并通过SimpleApplicationEventMulticaster向适合接收当前事件的ApplicationListener发布;
SimpleApplicationEventMulticaster发布事件时,会先获取出所有适合接收当前事件的ApplicationListener,然后调用这些ApplicationListener的onApplicationEvent() 方法,每一个ApplicationListener会在其实现的onApplicationEvent() 方法中完成对事件的处理。
图示如下。
四. 外部化配置加载
Springboot启动时,会在调用运行时监听器的starting() 方法后创建
DefaultApplicationArguments对象,然后就会开始加载外部化配置。
外部化配置通常由application.yml文件(或者application.properties文件)提供,在application.yml文件中添加配置项也是最常用的外部化配置方式。实际上为Springboot应用程序添加外部化配置的方式还有许多种,可以参考Springboot-外部化配置,下面是较为常用的外部化配置方式的优先级(由上到下优先级逐渐降低)。
命令行参数,即Command line arguments;
JAVA系统属性,即Java System properties(System#getProperties);
操作系统环境变量,即OS environment variables;
配置数据文件(例如application.yml文件),即Config data(such as application.properties files) jar包外指定了profile的配置数据文件:application-{profile}.yml jar包外的配置数据文件:application.yml jar包内指定了profile的配置数据文件:application-{profile}.yml jar包内的配置数据文件:application.yml
作用在由@Configuration注解修饰的类上的@PropertySource注解,即@PropertySource annotations on your @Configuration classes;
默认属性,即Default properties(specified by setting SpringApplication#setDefaultProperties)。
Springboot在启动过程中的SpringApplication#prepareEnvironment方法中会加载上述的外部化配置为Environment,Environment是Springboot外部化配置的入口,通过Environment可以获取到Springboot加载的所有外部化配置。
下图给出了SpringApplication#prepareEnvironment方法执行完后Environment的详细信息。
可见Environment的实际类型为
StandardServletEnvironment,这是和Springboot的应用程序类型挂钩,这点后面再说。
StandardServletEnvironment内部持有一个MutablePropertySources对象,该对象持有一个PropertySource的集合,Springboot加载的每一种外部化配置都会最终被解析为一个PropertySource的实现类并存放在MutablePropertySources的PropertySource集合中,PropertySource就是每一种外部化配置源在Springboot中的体现,其提供了对外部化配置的各种操作。根据上图为例,给出一部分外部化配置源与PropertySource的实现类的对应关系。
外部化配置
PropertySource
命令行参数
SimpleCommandLinePropertySource
JAVA系统属性
PropertiesPropertySource
操作系统环境变量
OriginAwareSystemEnvironmentPropertySource
配置数据文件
OriginTrackedMapPropertySource
启动程序时通过命令行指定的应用程序参数(args)会被先创建为
DefaultApplicationArguments对象,然后再被解析为
SimpleCommandLinePropertySource,例如通过IDEA进行如下配置。
那么对应的
SimpleCommandLinePropertySource如下所示。
如果在resources目录创建一个application.yml文件,且内容如下。server: port: 8080 address: 127.0.0.1
那么对应的
OriginTrackedMapPropertySource如下所示。
下面将从SpringApplication#prepareEnvironment方法为入口,对Springboot启动流程中的外部化配置加载进行简要分析。SpringApplication#prepareEnvironment方法如下所示。
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { // 创建ConfigurableEnvironment对象 ConfigurableEnvironment environment = getOrCreateEnvironment(); // 将命令行参数解析为PropertySource并加载到Environment中 configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); // 发布Environment准备好的事件 // 进一步加载更多的外部化配置到Environment中 listeners.environmentPrepared(bootstrapContext, environment); DefaultPropertiesPropertySource.moveToEnd(environment); configureAdditionalProfiles(environment); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment;}
在SpringApplication#prepareEnvironment方法中,首先会调用getOrCreateEnvironment() 方法创建ConfigurableEnvironment对象,创建出来的ConfigurableEnvironment的实际类型会根据SpringApplication初始化时推断出来的WEB应用程序类型而定,如果WEB应用程序类型为SERVLET,则创建出来的ConfigurableEnvironment实际类型为
StandardServletEnvironment,并且在初始化
StandardServletEnvironment时还会一并将JAVA系统属性和操作系统环境变量这两个外部化配置加载到
StandardServletEnvironment中。
在创建好
StandardServletEnvironment后,会再将命令行参数解析为PropertySource并加载到
StandardServletEnvironment中,随后就通过Springboot事件机制向ApplicationListener发布Environment准备好的事件,这里会接收该事件的ApplicationListener为
EnvironmentPostProcessorApplicationListener(2.4.0版本以前为
ConfigFileApplicationListener,该监听器从2.4.0版本起被废弃)。
接下来先分析一下getOrCreateEnvironment() 方法的实现。private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } // 根据WEB应用程序类型创建不同的ConfigurableEnvironment switch (this.webApplicationType) { case SERVLET: return new StandardServletEnvironment(); case REACTIVE: return new StandardReactiveWebEnvironment(); default: return new StandardEnvironment(); }}
StandardServletEnvironment的类图如下所示。
StandardServletEnvironment在初始化时会先调用到其父类AbstractEnvironment的构造方法,如下所示。
public AbstractEnvironment() { customizePropertySources(this.propertySources);}
实际会调用到
StandardServletEnvironment实现的customizePropertySources() 方法,如下所示。protected void customizePropertySources(MutablePropertySources propertySources) { // Servlet相关的外部化配置的加载 propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME)); propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)); if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) { propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME)); } // 调用父类StandardEnvironment实现的customizePropertySources()方法 super.customizePropertySources(propertySources);}
继续看StandardEnvironment实现的customizePropertySources() 方法,如下所示。
protected void customizePropertySources(MutablePropertySources propertySources) { // 将JAVA系统属性解析为PropertiesPropertySource,并加载到PropertySource集合中 propertySources.addLast( new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties())); // 将操作系统环境变量解析为SystemEnvironmentPropertySource,并加载到PropertySource集合中 propertySources.addLast( new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));}
到这里getOrCreateEnvironment() 方法做的事情分析完毕。
下面再分析一下
EnvironmentPostProcessorApplicationListener接收到Environment准备好的事件(
ApplicationEnvironmentPreparedEvent)后的执行流程,
EnvironmentPostProcessorApplicationListener的onApplicationEvent() 方法如下所示。public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationEnvironmentPreparedEvent) { // 事件event的实际类型为ApplicationEnvironmentPreparedEvent onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event); } if (event instanceof ApplicationPreparedEvent) { onApplicationPreparedEvent((ApplicationPreparedEvent) event); } if (event instanceof ApplicationFailedEvent) { onApplicationFailedEvent((ApplicationFailedEvent) event); }}
继续看
onApplicationEnvironmentPreparedEvent() 方法。
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) { ConfigurableEnvironment environment = event.getEnvironment(); SpringApplication application = event.getSpringApplication(); // 遍历所有EnvironmentPostProcessor的实现类,每个EnvironmentPostProcessor的实现类都会对相应的外部化配置做后置处理 // 处理配置数据文件的EnvironmentPostProcessor的实际类型为ConfigDataEnvironmentPostProcessor for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(event.getBootstrapContext())) { postProcessor.postProcessEnvironment(environment, application); }}
EnvironmentPostProcessor的继承树如下所示。
每一个EnvironmentPostProcessor的实现类都会对相应的外部化配置做后置处理,例如
RandomValuePropertySourceEnvironmentPostProcessor会加载一个RandomValuePropertySource到Environment中,
SystemEnvironmentPropertySourceEnvironmentPostProcessor会将Environment中的
SystemEnvironmentPropertySource替换为
SystemEnvironmentPropertySource的子类
OriginAwareSystemEnvironmentPropertySource。
在EnvironmentPostProcessor的实现类中,有一个较为重要的实现类叫做
ConfigDataEnvironmentPostProcessor,其可以将配置数据文件(application.yml等)加载为
OriginTrackedMapPropertySource并设置到Environment中。
至此,Springboot启动流程中的外部化配置加载分析完毕,下面是小结。
首先会根据初始化SpringApplication时推断出来的WEB应用程序类型创建不同的Environment,例如WEB应用程序类型为SERVLET时,创建的Environment的实际类型为StandardServletEnvironment;
在创建StandardServletEnvironment时,就会向StandardServletEnvironment中加载一部分外部化配置,在这个阶段加载的外部化配置主要是JAVA系统属性和操作系统环境变量;
在创建StandardServletEnvironment后,还会通过Springboot事件机制向EnvironmentPostProcessorApplicationListener发布ApplicationEnvironmentPreparedEvent事件,EnvironmentPostProcessorApplicationListener中收到ApplicationEnvironmentPreparedEvent事件后,会调用EnvironmentPostProcessor的实现类完成对Environment的后置处理,即会继续向Environment加载外部化配置,配置数据文件(application.yml等)的加载就在这个阶段完成;
StandardServletEnvironment内部持有一个MutablePropertySources对象,该对象持有一个PropertySource的集合,Springboot加载的每一种外部化配置都会最终被解析为一个PropertySource的实现类并存放在MutablePropertySources的PropertySource集合中,PropertySource就是每一种外部化配置源在Springboot中的体现,其提供了对外部化配置的各种操作。
总结
Springboot启动时,第一件重要事件就是初始化SpringApplication,并主要完成如下事情。
设置源。实际就是设置Spring容器启动时依赖的初始配置类,也就是Springboot中的启动类;
设置WEB应用程序类型。例如可以是SERVLET,REACTIVE等;
加载并设置Bootstrapper,ApplicationContextInitializer和ApplicationListener;
设置应用程序主类的Class对象。
然后Springboot启动时还会开启事件机制,主要就是通过运行时监听器
EventPublishingRunListener创建事件并分发给对应的ApplicationListener。
再然后会加载外部化配置,也就是得到很重要的Environment对象,通过Environment对象就可以拿到Springboot加载的所有外部化配置。
再然后会完成容器刷新,也就是执行Spring中的各种扩展点,初始化各种bean,这部分逻辑属于是Spring的逻辑,故本文并未详细介绍。除此之外,在容器刷新时,还会完成WEB容器的启动,例如启动Springboot内嵌的Tomcat,这部分内容比较多,会在后面单独进行分析。
最后,Springboot在整个启动流程中,会借助事件机制来发布各种事件,发布事件就是借助于上述提到的
EventPublishingRunListener,这是一个运行时监听器,是Springboot中提供的监听器,不要和Spring中的ApplicationListener混淆了。
如果觉得本篇文章对你有帮助,求求你点个赞,最后再点个关注吧。创作不易,感谢支持!
链接:
https://juejin.cn/post/7214831216028745783
联系客服