关系经济人类预测化学自然
投稿投诉
自然科学
知识物理
化学生物
地理解释
预测理解
本质社会
人类现象
行为研究
经济政治
心理结构
关系指导
人文遗产

详细分析SpringBoot启动流程

9月17日 桃花醉投稿
  组里的实习生妹妹讲了一次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
投诉 评论 转载

强者的潜质是什么见过世面的男人,吃的了西餐,也咽的下快餐头条创作挑战赛生活,一半烟火,一半清欢;人生,一半清醒,一半释然。见过世面的男人,吃的了西餐,也咽的下快餐,有皮鞋,也有帆布鞋,喝茶也喝酒。特别凶也可以特别温……巧治狂象英国北部有个苏格兰,苏格兰有个城市叫爱丁堡。爱丁堡风景优美,城里的公园众多,很受人们的喜爱。而其中的一个公园尤其受到大人孩子的青睐。因为在这个公园里有一个动物园,各种各样的动物……当前我国商业银行业务发展问题初步探讨【摘要】改革开放实践过程中,商业银行的业务发展日新月异,有力促进了宏观经济发展,直接表现在市场融资和投资两个方面,是企业和个人间接融资的主要渠道。整体上看,商业银行在金融行业中……中兴通讯助力陕西数字经济高质量发展3月21日,由陕西省通信管理局主办的陕西省信息通信行业5G规模化应用暨工业互联网推进大会在西安成功召开,大会旨在加快5G应用规模化发展,推动5G赋能千行百业。会上举行了聚能行动……详细分析SpringBoot启动流程组里的实习生妹妹讲了一次Springboot的启动,讲着讲着就讲到Spring的bean的生命周期去了,我心想坏了,这妮子估计把Springboot和Spri……MIUI14推送开启!为何几年前的小米11,竟被说成高人一等不得不说,在小米正式推出了MIUI14系统之后,之前有关于小米系统方面的吐槽,在网上确实是少了不少。特别是我身为一个现任的小米用户,在体验到了MIUI14系统之后,也确实能够感……2023中国城市品牌突破发展高峰论坛在北京成功举办中国小康网讯记者袁凯北京报道3月26日,2023中国城市品牌突破发展高峰论坛在北京成功举办。会议以换道超车:中国城市国际化视野与创新化突围为主题,提出重要理论研究成果:《塑造难……跑圈周报苏州马拉松开跑马拉松国家纪录被打破2023年苏州马拉松在今日开赛,这一届的苏州马拉松是苏州的第一届城市马拉松赛事,凭借着这座城市的知名度,苏马获得了许多跑友的青睐。赛事途经苏州著名水系和地标建筑,向跑者展……国足,惜败!对手排名105在北京时间26日中午结束的友谊赛中,国足客场1:2不敌新西兰,以1平1负结束了本周与对手的两场友谊赛。从比赛进程来看,新帅扬科维奇麾下的这支全新国足亮点有限,球队还需时间……女人种行为易诱发宫寒症女人月经受凉会致癌是真的吗?如果在月经时,洗头发、吃冰冷食物,会让污血残留在子宫之内,导致荷尔蒙分泌失调,而引发乳癌、子宫癌。这则消息听起来有几分道理,因为从青春期开始,……赏识造句用赏识造句大全91、我决断学摄影以便更能玩赏赏识自然的美。92、只有赏识你的人,才会给我们机会。你年轻,你热情,你精力和精子一样多,只是缺少经验值,缺少方法论。赏识你的人,会把经验和方……采购成本分析中小企业采购成本控制的秘诀材料采购成本在企业所有成本构成中所占比例最高,也是企业成本控制中最难的部分。一些企业一要求降价,供应商就立即停止供货,而另一些企业则承诺降价,但在产品中掺杂缺陷产品,甚至有的供……
病从口入!这些致癌食物你居然天天都在吃4。25娱乐爆料快本张哲瀚鹿晗关晓彤黄晓明港圈男星王宝泉蔡斌皆非最佳人选,中国女排选帅应该大胆求变,着眼未来哈登之前打不好,是累的,今天我让大哥3节打卡王宝强冯清被曝结婚,做了财产公证,马蓉讽刺私生子认祖归宗逆转!约基奇3718勇士31灰熊21森林狼?判断宝宝发育是否达标,看头就知道,有这些特征说明发育好教你3个方法,轻松应对血液粘稠从干饭人的角度看骑自行车穷游至少需要多少成本和携带些什么明日雨水,两类菜要常吃!健脾化湿,润肠通便,在家就能种郑恺苗苗官宣怀二胎!苗苗自驾去做产检,郑恺评论想回家一把吉他走天下,74岁的老人走向舞台,全场轰动
中国女性独自行走110国游记之4I旅行专利权自动终止还能再申请吗黑人与白人的故事帮助流浪狗看图写话三年级上册有余数的除法教学反思范文突发,又一巨头出事:高管“团灭”热传聚热点网 老人求婚被拒聪明的女人,懂得规划自己的婚姻诚信最珍贵花开湖北诗意中国在这里郁见浪漫中式新婚祝福语话2021年童鞋黑名单这5种鞋,再好看也别给孩子穿一大步造句用一大步造句大全

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找菏泽德阳山西湖州宝鸡上海茂名内江三亚信阳长春北海西安安徽黄石烟台沧州湛江肇庆鹤壁六安韶关成都钦州