组里的实习生妹妹讲了一次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()方法说明,如下所示。publicConfigurableApplicationContextrun(String。。。args){创建StopWatch,用于统计Springboot启动的耗时StopWatchstopWatchnewStopWatch();开始计时stopWatch。start();DefaultBootstrapContextbootstrapContextcreateBootstrapContext();ConfigurableApplicationCconfigureHeadlessProperty();获取运行时监听器SpringApplicationRunListenerslistenersgetRunListeners(args);调用运行时监听器的starting()方法该方法需要在Springboot一启动时就调用,用于特别早期的初始化listeners。starting(bootstrapContext,this。mainApplicationClass);try{获取args参数对象ApplicationArgumentsapplicationArgumentsnewDefaultApplicationArguments(args);读取Springboot配置文件并创建Environment对象这里创建的Environment对象实际为ConfigurableEnvironmentConfigurableEnvironmentenvironmentprepareEnvironment(listeners,bootstrapContext,applicationArguments);configureIgnoreBeanInfo(environment);打印Banner图标BannerprintedBannerprintBanner(environment);创建ApplicationContext应用行下文,即创建容器contextcreateApplicationContext();context。setApplicationStartup(this。applicationStartup);准备容器prepareContext(bootstrapContext,context,environment,listeners,applicationArguments,printedBanner);初始化容器refreshContext(context);afterRefresh(context,applicationArguments);停止计时stopWatch。stop();if(this。logStartupInfo){打印启动耗时等信息newStartupInfoLogger(this。mainApplicationClass)。logStarted(getApplicationLog(),stopWatch);}调用运行时监听器的started()方法该方法需要在应用程序启动后,CommandLineRunners和ApplicationRunners被调用前执行listeners。started(context);callRunners(context,applicationArguments);}catch(Throwableex){handleRunFailure(context,ex,listeners);thrownewIllegalStateException(ex);}try{调用运行时监听器的running()方法该方法需要在SpringApplication的run()方法执行完之前被调用listeners。running(context);}catch(Throwableex){handleRunFailure(context,ex,null);thrownewIllegalStateException(ex);}}二。SpringApplication的初始化 通常,Springboot应用程序的启动类定义如下。SpringBootApplicationpublicclassLearnStartApplication{publicstaticvoidmain(String〔〕args){SpringApplication。run(LearnStartApplication。class,args);}} 从SpringApplication的静态run()方法一路跟进,会发现如下的实现。publicstaticConfigurableApplicationContextrun(C?primarySource,String。。。args){returnrun(newC?〔〕{primarySource},args);}publicstaticConfigurableApplicationContextrun(C?〔〕primarySources,String〔〕args){returnnewSpringApplication(primarySources)。run(args);} 也就是Springboot启动时会先创建SpringApplication,然后再通过SpringApplication的run()方法完成启动。所以下面分析一下SpringApplication的初始化逻辑,其构造方法如下所示。publicSpringApplication(ResourceLoaderresourceLoader,C?。。。primarySources){this。resourceLoaderresourceLAssert。notNull(primarySources,PrimarySourcesmustnotbenull);设置源通常Springboot的启动类就是源this。primarySourcesnewLinkedHashSet(Arrays。asList(primarySources));推断并设置WEB应用程序类型根据classpath下的类来推断this。webApplicationTypeWebApplicationType。deduceFromClasspath();加载并设置Bootstrapperthis。bootstrappersnewArrayList(getSpringFactoriesInstances(Bootstrapper。class));加载并设置初始化器setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer。class));加载并设置应用事件监听器setListeners((Collection)getSpringFactoriesInstances(ApplicationListener。class));推断并设置应用程序主类的Class对象this。mainApplicationClassdeduceMainApplicationClass();} 梳理一下在SpringApplication的构造方法中,做了如下事情。设置源。通常,Springboot中的源就是Springboot的启动类;设置WEB应用程序类型。通过判断classpath下是否存在某些类,来推断当前WEB应用程序的类型;加载并设置Bootstrapper,ApplicationContextInitializer和ApplicationListener。借助SpringFactoriesLoader基于SPI机制完成Bootstrapper,ApplicationContextInitializer和ApplicationListener的加载,然后设置到SpringApplication中;设置应用程序主类的Class对象。 下面对上述事情进行分析。1。设置源 这里的源,也就是Spring容器启动时依赖的初始配置类,在Springboot中,初始配置类通常为启动类。下面可以通过调试看一下primarySources字段的值,如下所示。 可见源就是Springboot的启动类的Class对象。2。设置WEB应用程序类型 WebApplicationTypededuceFromClasspath方法如下所示。privatestaticfinalString〔〕SERVLETINDICATORCLASSES{javax。servlet。Servlet,org。springframework。web。context。ConfigurableWebApplicationContext};privatestaticfinalStringWEBMVCINDICATORCLASSorg。springframework。web。servlet。DispatcherSprivatestaticfinalStringWEBFLUXINDICATORCLASSorg。springframework。web。reactive。DispatcherHprivatestaticfinalStringJERSEYINDICATORCLASSorg。glassfish。jersey。servlet。ServletCprivatestaticfinalStringSERVLETAPPLICATIONCONTEXTCLASSorg。springframework。web。context。WebApplicationCprivatestaticfinalStringREACTIVEAPPLICATIONCONTEXTCLASSorg。springframework。boot。web。reactive。context。ReactiveWebApplicationCstaticWebApplicationTypededuceFromClasspath(){if(ClassUtils。isPresent(WEBFLUXINDICATORCLASS,null)!ClassUtils。isPresent(WEBMVCINDICATORCLASS,null)!ClassUtils。isPresent(JERSEYINDICATORCLASS,null)){classpath下存在DispatcherHandler,但不存在DispatcherServlet和ServletContainer则WEN应用程序类型推断为REACTIVE,即响应式WEB应用程序returnWebApplicationType。REACTIVE;}for(StringclassName:SERVLETINDICATORCLASSES){if(!ClassUtils。isPresent(className,null)){非WEB应用程序returnWebApplicationType。NONE;}}基于Servlet的WEB应用程序returnWebApplicationType。SERVLET;} 在WebApplicationType中预定义了若干种用于判断的类的全限定名,然后在deduceFromClasspath()方法中使用ClassUtils来判断预定义的类是否存在,通过这样的方式最终可以推断出当前WEB应用程序类型。在示例工程中,如果只引入springbootstarter包,那么推断出来的WebApplicationType为NONE,如下所示。 如果再引入springbootstarterweb包,则推断出来的WebApplicationType为SERVLET,如下所示。 3。加载并设置Bootstrapper,ApplicationContextInitializer和ApplicationListener 这里主要分析一下是如何加载Bootstrapper,ApplicationContextInitializer和ApplicationListener的。它们的加载均使用了getSpringFactoriesInstances()方法,下面看一下实现。privateTCollectionTgetSpringFactoriesInstances(ClassTtype){returngetSpringFactoriesInstances(type,newC?〔〕{});}privateTCollectionTgetSpringFactoriesInstances(ClassTtype,C?〔〕parameterTypes,Object。。。args){ClassLoaderclassLoadergetClassLoader();通过SpringFactoriesLoader扫描classpath所有jar包的METAINF目录下的spring。factories文件将type全限定名对应的全限定名的集合获取到SetStringnamesnewLinkedHashSet(SpringFactoriesLoader。loadFactoryNames(type,classLoader));实例化ListTinstancescreateSpringFactoriesInstances(type,parameterTypes,classLoader,args,names);AnnotationAwareOrderComparator。sort(instances);} 主要就是基于SpringFactoriesLoader完成加载,加载机制和Springboot中的自动装配是一样,唯一的区别就是自动装配中在spring。factories文件中是根据EnableAutoConfiguration的全限定名作为key去获取全限定名集合,而在这里是根据Bootstrapper,ApplicationContextInitializer和ApplicationListener的全限定名作为key去获取全限定名集合,以springbootautoconfigure包中的spring。factories文件为例,说明如下。org。springframework。context。ApplicationContextInitializerorg。springframework。boot。autoconfigure。SharedMetadataReaderFactoryContextInitializer,org。springframework。boot。autoconfigure。logging。ConditionEvaluationReportLoggingListenerorg。springframework。context。ApplicationListenerorg。springframework。boot。autoconfigure。BackgroundPreinitializer4。设置应用程序主类的Class对象 获取应用程序主类的Class对象的SpringApplicationdeduceMainApplicationClass方法如下所示。privateC?deduceMainApplicationClass(){try{通过RuntimeException获取堆栈StackTraceElement〔〕stackTracenewRuntimeException()。getStackTrace();for(StackTraceElementstackTraceElement:stackTrace){判断堆栈元素的发生方法名是否为mainif(main。equals(stackTraceElement。getMethodName())){通过反射获取到main方法所在类的Class对象returnClass。forName(stackTraceElement。getClassName());}}}catch(ClassNotFoundExceptionex){}} 获取应用程序主类的Class对象是通过堆栈实现的,下面给出调试截图。 三。Springboot事件机制 在Springboot启动的一开始,有一步逻辑是获取运行时监听器,最终会获取到一个SpringApplicationRunListeners对象,下面看一下获取运行时监听器的getRunListeners()方法的实现。privateSpringApplicationRunListenersgetRunListeners(String〔〕args){C?〔〕typesnewC?〔〕{SpringApplication。class,String〔〕。class};先基于SpringFactoriesLoader的SPI机制获取SpringApplicationRunListener的实现类集合然后创建SpringApplicationRunListeners对象returnnewSpringApplicationRunListeners(logger,getSpringFactoriesInstances(SpringApplicationRunListener。class,types,this,args),this。applicationStartup);}SpringApplicationRunListeners(Loglog,C?extendsSpringApplicationRunListenerlisteners,ApplicationStartupapplicationStartup){SpringApplicationRunListeners的构造方法中只是进行简单赋值this。this。listenersnewArrayList(listeners);this。applicationStartupapplicationS} 在getRunListeners()方法中会先基于SpringFactoriesLoader的SPI机制将SpringApplicationRunListener接口的实现类获取出来,在springboot包中提供了一个SpringApplicationRunListener接口的实现类,为EventPublishingRunListener,这也是Springboot提供的唯一一个内置运行时监听器,所以通过getRunListeners()方法获取到的SpringApplicationRunListeners对象中持有一个SpringApplicationRunListener的集合,这个集合中默认情况下一定会包含一个EventPublishingRunListener的对象。 下面再以SpringApplicationRunListeners的starting()方法为例,分析一下SpringApplicationRunListeners是如何工作的。voidstarting(ConfigurableBootstrapContextbootstrapContext,C?mainApplicationClass){doWithListeners(spring。boot。application。starting,(listener)listener。starting(bootstrapContext),(step){if(mainApplicationClass!null){step。tag(mainApplicationClass,mainApplicationClass。getName());}});}privatevoiddoWithListeners(StringstepName,ConsumerSpringApplicationRunListenerlistenerAction,ConsumerStartupStepstepAction){StartupStepstepthis。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()方法为例,进行说明。publicvoidstarting(ConfigurableBootstrapContextbootstrapContext){先创建一个ApplicationStartingEvent事件对象然后调用SimpleApplicationEventMulticaster来发布事件对象this。initialMulticaster。multicastEvent(newApplicationStartingEvent(bootstrapContext,this。application,this。args));} EventPublishingRunListener的starting()方法中会先创建ApplicationStartingEvent事件对象,然后通过EventPublishingRunListener持有的一个SimpleApplicationEventMulticaster对象来发布事件。 那么下面继续分析SimpleApplicationEventMulticaster怎么发布事件,发布给谁,SimpleApplicationEventMulticaster的multicastEvent()方法如下所示。publicvoidmulticastEvent(ApplicationEventevent){multicastEvent(event,resolveDefaultEventType(event));}publicvoidmulticastEvent(finalApplicationEventevent,NullableResolvableTypeeventType){ResolvableTypetype(eventType!null?eventType:resolveDefaultEventType(event));ExecutorexecutorgetTaskExecutor();调用getApplicationListeners()方法将所有适合接收当前事件的ApplicationListener获取出来然后基于异步或者同步的方式向符合条件的ApplicationListener发布事件for(ApplicationL?listener:getApplicationListeners(event,type)){if(executor!null){异步发布executor。execute(()invokeListener(listener,event));}else{同步发布invokeListener(listener,event);}}} SimpleApplicationEventMulticaster的multicastEvent()方法中会先将初始化SpringApplication时加载的ApplicationListener获取到,然后遍历其中适合接收当前事件的ApplicationListener,然后异步或者同步的向ApplicationListener发布事件,继续看invokeListener()方法,如下所示。protectedvoidinvokeListener(ApplicationL?listener,ApplicationEventevent){ErrorHandlererrorHandlergetErrorHandler();实际调用doInvokeListener()方法来向ApplicationListener发布事件if(errorHandler!null){try{doInvokeListener(listener,event);}catch(Throwableerr){errorHandler。handleError(err);}}else{doInvokeListener(listener,event);}}privatevoiddoInvokeListener(ApplicationListenerlistener,ApplicationEventevent){try{ApplicationListener接口的实现类都会实现onApplicationEvent()方法在onApplicationEvent()方法中会处理当前接收到的事件listener。onApplicationEvent(event);}catch(ClassCastExceptionex){Stringmsgex。getMessage();if(msgnullmatchesClassCastMessage(msg,event。getClass())){LogloggerLogFactory。getLog(getClass());if(logger。isTraceEnabled()){logger。trace(Nonmatchingeventtypeforlistener:listener,ex);}}else{}}} SimpleApplicationEventMulticaster的invokeListener()方法中实际会调用到doInvokeListener()方法,在doInvokeListener()方法中会调用ApplicationListener的onApplicationEvent()方法,所以在这里就调用到了ApplicationListener实际处理事件的逻辑。 现在对Springboot中的事件监听机制进行小结。SpringApplication初始化时会加载所有的ApplicationL在Springboot启动的一开始,会调用到SpringApplicationgetRunListeners方法创建一个SpringApplicationRunListeners对象;SpringApplicationRunListeners是组合模式的应用,其持有一个SpringApplicationRunListener的集合,集合中默认会存在一个Springboot提供的SpringApplicationRunListener的实现类EventPublishingRunListener,所有对SpringApplicationRunListeners的调用请求都会被传递给集合中的每一个SpringApplicationRunLEventPublishingRunListener中持有一个事件发布器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外部化配置,下面是较为常用的外部化配置方式的优先级(由上到下优先级逐渐降低)。命令行参数,即CJAVA系统属性,即JavaSystemproperties(SystemgetProperties);操作系统环境变量,即OS配置数据文件(例如application。yml文件),即Configdata(suchasapplication。propertiesfiles)jar包外指定了profile的配置数据文件:application{profile}。ymljar包外的配置数据文件:application。ymljar包内指定了profile的配置数据文件:application{profile}。ymljar包内的配置数据文件:application。yml作用在由Configuration注解修饰的类上的PropertySource注解,即PropertySourceannotationsonyourC默认属性,即Defaultproperties(specifiedbysettingSpringApplicationsetDefaultProperties)。 Springboot在启动过程中的SpringApplicationprepareEnvironment方法中会加载上述的外部化配置为Environment,Environment是Springboot外部化配置的入口,通过Environment可以获取到Springboot加载的所有外部化配置。 下图给出了SpringApplicationprepareEnvironment方法执行完后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:8080address:127。0。0。1 那么对应的OriginTrackedMapPropertySource如下所示。 下面将从SpringApplicationprepareEnvironment方法为入口,对Springboot启动流程中的外部化配置加载进行简要分析。SpringApplicationprepareEnvironment方法如下所示。privateConfigurableEnvironmentprepareEnvironment(SpringApplicationRunListenerslisteners,DefaultBootstrapContextbootstrapContext,ApplicationArgumentsapplicationArguments){创建ConfigurableEnvironment对象ConfigurableEnvironmentenvironmentgetOrCreateEnvironment();将命令行参数解析为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){environmentnewEnvironmentConverter(getClassLoader())。convertEnvironmentIfNecessary(environment,deduceEnvironmentClass());}ConfigurationPropertySources。attach(environment);} 在SpringApplicationprepareEnvironment方法中,首先会调用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()方法的实现。privateConfigurableEnvironmentgetOrCreateEnvironment(){if(this。environment!null){returnthis。}根据WEB应用程序类型创建不同的ConfigurableEnvironmentswitch(this。webApplicationType){caseSERVLET:returnnewStandardServletEnvironment();caseREACTIVE:returnnewStandardReactiveWebEnvironment();default:returnnewStandardEnvironment();}} StandardServletEnvironment的类图如下所示。 StandardServletEnvironment在初始化时会先调用到其父类AbstractEnvironment的构造方法,如下所示。publicAbstractEnvironment(){customizePropertySources(this。propertySources);} 实际会调用到StandardServletEnvironment实现的customizePropertySources()方法,如下所示。protectedvoidcustomizePropertySources(MutablePropertySourcespropertySources){Servlet相关的外部化配置的加载propertySources。addLast(newStubPropertySource(SERVLETCONFIGPROPERTYSOURCENAME));propertySources。addLast(newStubPropertySource(SERVLETCONTEXTPROPERTYSOURCENAME));if(JndiLocatorDelegate。isDefaultJndiEnvironmentAvailable()){propertySources。addLast(newJndiPropertySource(JNDIPROPERTYSOURCENAME));}调用父类StandardEnvironment实现的customizePropertySources()方法super。customizePropertySources(propertySources);} 继续看StandardEnvironment实现的customizePropertySources()方法,如下所示。protectedvoidcustomizePropertySources(MutablePropertySourcespropertySources){将JAVA系统属性解析为PropertiesPropertySource,并加载到PropertySource集合中propertySources。addLast(newPropertiesPropertySource(SYSTEMPROPERTIESPROPERTYSOURCENAME,getSystemProperties()));将操作系统环境变量解析为SystemEnvironmentPropertySource,并加载到PropertySource集合中propertySources。addLast(newSystemEnvironmentPropertySource(SYSTEMENVIRONMENTPROPERTYSOURCENAME,getSystemEnvironment()));} 到这里getOrCreateEnvironment()方法做的事情分析完毕。 下面再分析一下EnvironmentPostProcessorApplicationListener接收到Environment准备好的事件(ApplicationEnvironmentPreparedEvent)后的执行流程,EnvironmentPostProcessorApplicationListener的onApplicationEvent()方法如下所示。publicvoidonApplicationEvent(ApplicationEventevent){if(eventinstanceofApplicationEnvironmentPreparedEvent){事件event的实际类型为ApplicationEnvironmentPreparedEventonApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent)event);}if(eventinstanceofApplicationPreparedEvent){onApplicationPreparedEvent((ApplicationPreparedEvent)event);}if(eventinstanceofApplicationFailedEvent){onApplicationFailedEvent((ApplicationFailedEvent)event);}} 继续看onApplicationEnvironmentPreparedEvent()方法。privatevoidonApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEventevent){ConfigurableEnvironmentenvironmentevent。getEnvironment();SpringApplicationapplicationevent。getSpringApplication();遍历所有EnvironmentPostProcessor的实现类,每个EnvironmentPostProcessor的实现类都会对相应的外部化配置做后置处理处理配置数据文件的EnvironmentPostProcessor的实际类型为ConfigDataEnvironmentPostProcessorfor(EnvironmentPostProcessorpostProcessor: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的实际类型为StandardServletE在创建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和ApplicationL设置应用程序主类的Class对象。 然后Springboot启动时还会开启事件机制,主要就是通过运行时监听器EventPublishingRunListener创建事件并分发给对应的ApplicationListener。 再然后会加载外部化配置,也就是得到很重要的Environment对象,通过Environment对象就可以拿到Springboot加载的所有外部化配置。 再然后会完成容器刷新,也就是执行Spring中的各种扩展点,初始化各种bean,这部分逻辑属于是Spring的逻辑,故本文并未详细介绍。除此之外,在容器刷新时,还会完成WEB容器的启动,例如启动Springboot内嵌的Tomcat,这部分内容比较多,会在后面单独进行分析。 最后,Springboot在整个启动流程中,会借助事件机制来发布各种事件,发布事件就是借助于上述提到的EventPublishingRunListener,这是一个运行时监听器,是Springboot中提供的监听器,不要和Spring中的ApplicationListener混淆了。 如果觉得本篇文章对你有帮助,求求你点个赞,最后再点个关注吧。创作不易,感谢支持! 链接:https:juejin。cnpost7214831216028745783