SpringBoot启动过程解析(简化)
springBoot web方式啟動過程
在這個啟動過程中會有各種SpringBoot對外提供的擴展接口來對不同啟動階段進行自定義操作。 了解啟動過程也是為了讓我們更好理解SpringBoot提供的擴展接口使用
jar包啟動或者外置war包啟動都是調(diào)用SpringApplication.run()方法進行項目啟動
tomcat會查詢context上下文中實現(xiàn)ServletContainerInitializer接口的類,然后調(diào)用類的onStartup(Set<Class<?>> c, ServletContext ctx)方法 Spring的SpringServletContainerInitializer實現(xiàn)了這個ServletContainerInitializer接口, 會獲取WebApplicationInitializer接口的實現(xiàn)類,調(diào)用onStartup()方法 SpringBoot的類SpringBootServletInitializer實現(xiàn)了Spring的WebApplicationInitializer擴展接口, 會在onStartup()方法中創(chuàng)建SpringApplication類,并調(diào)用SpringApplication.run()來完成啟動項目 與我們在開發(fā)時調(diào)用Application.main()方法啟動時一樣的原理
開始分析
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
this.setRegisterErrorPageFilter(false); // 錯誤頁面有容器來處理,而不是SpringBoot
return builder.sources(Application.class);
}
}
1、SpringApplication構(gòu)造方法
初始化 ApplicationListener 和 ApplicationContextInitializer,還有設(shè)置應(yīng)用類型:默認為WebApplicationType.SERVLET 即servlet容器
接著執(zhí)行SpringApplication.run()方法
在這個方法里面總的概述就是對上下文和環(huán)境進行初始化配置,application上下文的BeanFactory管理bean定義和bean的初始化過程。。
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
2 、觸發(fā)ApplicationStartingEvent事件
SpringBoot對Spring的ApplicationListener進行了一個包裝
在啟動過程中使用SpringApplicationRunListener接口的實現(xiàn)類EventPublishingRunListener來觸發(fā)各種事件
public interface SpringApplicationRunListener {
starting ==> ApplicationStartingEvent
environmentPrepared ==> ApplicationEnvironmentPreparedEvent
contextPrepared ==> 無
contextLoaded ==》 ApplicationPreparedEvent
started ==》 ApplicationStartedEvent
running ==》 ApplicationReadyEvent
failed ==》 ApplicationFailedEvent
}
我們可以通過自定義SpringApplicationRunListener來在不同階段操作,也可以通過配置ApplicationListener來完成不同階段的操作。
這個開始啟動事件觸發(fā)操作:主要是進行日志系統(tǒng)初始化,字符集設(shè)置等操作
最開始觸發(fā)的監(jiān)聽器:LoggingApplicationListener和BackgroundPreinitializer
LoggingApplicationListener日志系統(tǒng)實例生成,執(zhí)行初始化前方法loggingSystem.beforeInitialize() BackgroundPreinitializer 后臺線程預(yù)初始化任務(wù),用于耗時任務(wù)的初始化 1.默認類型轉(zhuǎn)換器,format格式解析初始化 2.初始化校驗器 3. 默認處理表單數(shù)據(jù)的消息轉(zhuǎn)換器初始化的AllEncompassingFormHttpMessageConverter 4.初始化tomcat的MBeanFactory 5. 字符集初始化
3、獲取命令行參數(shù)和準(zhǔn)備上下文環(huán)境
prepareEnvironment(listeners, applicationArguments)
初始化systemProperties,systemEnvironment屬性源。獲取系統(tǒng)屬性設(shè)置到環(huán)境中
這個方法里觸發(fā)ApplicationEnvironmentPreparedEvent環(huán)境準(zhǔn)備完成事件監(jiān)聽器
主要是ConfigFileApplicationListener監(jiān)聽這個事件 會調(diào)用EnvironmentPostProcessor 環(huán)境后置處理器接口 還有這個類本身后置處理器會將配置文件加載到environment環(huán)境的屬性源列表中
環(huán)境準(zhǔn)備事件觸發(fā)操作:配置文件屬性源加載,日志系統(tǒng)配置環(huán)境屬性設(shè)置
EnvironmentPostProcessor 這個接口擴展處理上下文環(huán)境操作
DelegatingApplicationListener,這個委托代理監(jiān)聽器只能代理ApplicationEnvironmentPreparedEvent環(huán)境準(zhǔn)備完成事件
4、初始化上下文信息
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
為context上下文設(shè)置reader和scanner,用于獲取bean定義 調(diào)用ApplicationContextInitializer接口,對上下文進行初始化 將ApplicationArguments和SpringApplication作為bean注冊到BeanFactory中 load(context, sources.toArray(new Object[0])); 這里是將應(yīng)用啟動類,即Application.java類,將其生成BeanDefinition即bean定義,綁定到beanFactory中 觸發(fā)ApplicationPreparedEvent事件
ApplicationContextInitializer接口, 對上下文進行初始化操作
觸發(fā)ApplicationPreparedEvent事件
網(wǎng)上資料:ApplicationPreparedEvent事件:在Bean定義加載之后、bean初始化之前,刷新上下文之前觸發(fā)
自己調(diào)試:這個bean定義加載只是對Application.java啟動類和內(nèi)置的bean定義進行加載,我們項目中自定義的bean定義還是沒有進行加載。
ApplicationPreparedEvent事件觸發(fā)操作:
將ConfigFileApplicationListener # PropertySourceOrderingPostProcessor添加到ApplicationContext的BeanFactoryPostProcessors列表中,這個是BeanFactory的后置處理器 將Bean名稱為:“springBootLoggingSystem”日志系統(tǒng)注冊到beanfactory中
注意:在這里所有自定義的Bean定義都還沒有開始加載,這個進行自定義獲取bean是無效的
5、刷新上下文
refreshContext(context);
這個是spring啟動過程中最多邏輯的階段
5.1、 prepareRefresh()
設(shè)置系統(tǒng)啟動標(biāo)志為啟動,以及屬性校驗等操作
5.2、prepareBeanFactory(beanFactory)
配置beanfactory 設(shè)置bean類加載器,設(shè)置SpEL表達式解析器,注冊配置屬性編輯器, 初始化BeanPostProcessor(如ApplicationContextAwareProcessor), 設(shè)置忽略自動裝配的接口, 注冊默認環(huán)境bean(environment,systemProperties,systemEnvironment)
設(shè)置忽略自動裝配的接口是Spring默認指定的Aware及其子接口
當(dāng)類A中有屬性是類B時,在初始化A的bean時, 如果類B是實現(xiàn)了Aware或其子接口時。不會為A自動注入類B
而是要通過B實現(xiàn)了Aware其子接口的接口方法來為A設(shè)置屬性
一般來說:實現(xiàn)了Aware其子接口的實現(xiàn)類都是作為工具類,或者配置類
BeanPostProcessor接口:對bean初始化前后做一些操作邏輯,比如設(shè)置bean的屬性,或?qū)μ厥獾腷ean的方法進行調(diào)用等
5.3postProcessBeanFactory(beanFactory)
context的子類在配置好BeanFactory后的擴展方法,
這里是AnnotationConfigServletWebServerApplicationContext類 添加了一個后置處理器,一個忽略自動注入接口
beanFactory.addBeanPostProcessor(WebApplicationContextServletContextAwareProcessor) beanFactory.ignoreDependencyInterface(ServletContextAware.class)
5.4invokeBeanFactoryPostProcessors(beanFactory)
回調(diào)BeanFactoryPostProcessor處理器
在這里會進行bean定義的加載
會將ApplicationContext中設(shè)置的beanFactoryPostProcessors列表
轉(zhuǎn)換成BeanDefinitionRegistryPostProcessor(registryProcessors 注冊處理器) 和BeanFactoryPostProcessor(regularPostProcessors 規(guī)則處理器)
內(nèi)置beanFactory后置處理器列表
SharedMetadataReaderFactoryContextInitializer # CachingMetadataReaderFactoryPostProcessor ConfigurationWarningsApplicationContextInitializer # ConfigurationWarningsPostProcessor ConfigFileApplicationListener # PropertySourceOrderingPostProcessor
會先調(diào)用內(nèi)置的注冊處理器的postProcessBeanDefinitionRegistry(registry);方法
進行自定義bean的注冊,或者對beanFactory中的bean定義的屬性值進行修改
比如
SharedMetadataReaderFactoryContextInitializer # CachingMetadataReaderFactoryPostProcessor 會將SharedMetadataReaderFactoryContextInitializer.SharedMetadataReaderFactoryBean類創(chuàng)建bean定義注冊到BeanFactory中,
還有修改ConfigurationAnnotationProcessor類的metadataReaderFactory屬性值為SharedMetadataReaderFactoryBean
(重點)執(zhí)行完后置處理器的注冊方法后,會獲取到Beanfactory中綁定的BeanDefinitionRegistryPostProcessor
也就是ConfigurationClassPostProcessor,調(diào)用它的postProcessBeanDefinitionRegistry(registry)方法
這個類會將Spring注解標(biāo)記的Bean定義解析綁定到BeanFactory中
也就是說只要有@Configuration、@Component、@ComponentScan、@Import、@ImportResource和@Bean中的其中一個注解,就會將bean注冊到beanfactory中
@Import注解處理,如果導(dǎo)入的是ImportSelector接口實現(xiàn)類,會調(diào)用接口方法String[] selectImports() 通過返回字符串?dāng)?shù)組。里面是配置類名稱。動態(tài)選擇要導(dǎo)入的配置類全名
除了解析上面注解定義的bean外
還會處理@PropertySource注解,將指定的配置屬性源設(shè)置到environment環(huán)境中
this.reader.loadBeanDefinitions(configClasses);這里會進行額外的bean定義加載:(被Import導(dǎo)入的類加載,方法定義的bean加載,資源文件中定義的bean)
后置處理器處理過程中會被調(diào)用三次
1. 第一次是執(zhí)行ConfigurationClassPostProcessor后置處理器,獲取項目中的bean定義 執(zhí)行過了后置處理器不會再次執(zhí)行 2. 第二次是在所有bean定義中獲取有加@Order注解的BeanDefinitionRegistryPostProcessor實現(xiàn)類,執(zhí)行后置處理器。 3. 第三次是再次獲取沒有執(zhí)行過的處理器進行再次執(zhí)行 比如mybatis的MapperScannerConfigurer就是在這里執(zhí)行實現(xiàn)mybatis注解的bean定義加載 4. 最后是執(zhí)行registryProcessors注冊器和regularPostProcessors規(guī)則處理器的postProcessBeanFactory(beanFactory)方法
實現(xiàn)不同接口分類按從上到下執(zhí)行
PriorityOrdered接口 PropertySourcesPlaceholderConfigurer Ordered接口 沒有實現(xiàn)上面接口的 org.springframework.boot.context.properties.ConfigurationBeanFactoryMetadata preserveErrorControllerTargetClassPostProcessor dataSourceInitializerSchedulerDependencyPostProcessor
5.5、registerBeanPostProcessors(beanFactory);
從beanFactory獲取BeanPostProcessor分別按照PriorityOrdered,Ordered,普通的類型順序注冊BeanPostProcessor
beanFactory.addBeanPostProcessor(postProcessor)
5.6、initMessageSource():一般是我們用來初始化我們國際化文件的
5.7、initApplicationEventMulticaster(); 初始化事件廣播器
5.8、onRefresh():初始化其他的子容器類中的bean,同時創(chuàng)建spring的內(nèi)置tomcat,
ServletWebServerApplicationContext.onRefresh()在這里創(chuàng)建createWebServer
這里會調(diào)用tomcat的內(nèi)置啟動流程tomcat.start()
StandardServer->StandardService->StandardEngine(servlet引擎)->StandardHost(可以理解為ip+端口號)->StandardContext(這里就是我們的項目)
5.9、registerListeners():
添加用戶設(shè)置applicationListeners,然后從beanFactory獲取ApplicationListener, 然后發(fā)布需要earlyApplicationEvents事件 是從beanFactory中獲取所有實現(xiàn)ApplicationListener接口的類, getBeanNamesForType(ApplicationListener.class, true, false) 第一個type參數(shù)表示類型,第二個參數(shù)是否包含非單例的bean 第三個參數(shù)是否允許提前初始化, true表示允許。會導(dǎo)致bean提前初始化, false,表示不允許提前初始化,如果實在spring啟動過程中,這個參數(shù)需要設(shè)置為false
6.0、finishBeanFactoryInitialization(beanFactory);
緩存所有bean定義元數(shù)據(jù) bean definition metadata beanFactory.preInstantiateSingletons(); 實例化所有剩余的(非延遲初始化)單例。 在這個過程中會調(diào)用beanPostProcessors,bean實例化前后的處理器
調(diào)用AbstractAutowireCapableBeanFactory的 initializeBean(beanName, exposedObject, mbd);方法
在這個方法里會處理實現(xiàn)Aware接口的Bean類 invokeAwareMethods(beanName, bean);方法會處理實現(xiàn)了BeanNameAware,BeanClassLoaderAware,BeanFactoryAware接口實現(xiàn)了類的接口方法 接著調(diào)用applyBeanPostProcessorsBeforeInitialization 處理BeanPostProcessor bean調(diào)用初始化之前的處理 接著調(diào)用bean的初始化方法invokeInitMethods(beanName, wrappedBean, mbd); 接著調(diào)用bean的初始化方法之后的后置處理器
ApplicationContextAwareProcessor:
處理實現(xiàn)EnvironmentAware,EmbeddedValueResolverAware,ResourceLoaderAware,ApplicationEventPublisherAware,MessageSourceAware,ApplicationContextAware這些接口的實現(xiàn)bean類在 在初始化回調(diào)之前調(diào)用對應(yīng)接口方法做對應(yīng)的操作。 比如initializingBean的afterPropertiesSet()方法之前或@Bean注解設(shè)置的init-method之前調(diào)用
WebApplicationContextServletContextAwareProcessor
處理實現(xiàn)了ServletContextAware接口的bean,可以通過setServletContext(ServletContext)方法獲取servletContext
6.1、finishRefresh();
完成上下文刷新, 清除上下文級別的resources緩存(MetadataReader封裝的資源,比如掃描的配置類) 初始化lifecycleProcessor,用來管理spring生命周期的onRefresh作用是容器啟動成功,onClose是只應(yīng)用要關(guān)閉的時候
然后觸發(fā)ContextRefreshedEvent事件
refreshContext(context);執(zhí)行完成
到這里spring項目容器已經(jīng)啟動成功
7、afterRefresh(context, applicationArguments);
這里沒有操作
8、觸發(fā)ApplicationStartedEvent事件
9、callRunners(context, applicationArguments)
從beanfactory中獲取ApplicationRunner和CommandLineRunner接口實現(xiàn)類
調(diào)用他們的run方法
10、觸發(fā)ApplicationReadyEvent事件
啟動完成
作者:海綿般汲取
出處:https://www.cnblogs.com/gne-hwz/
版權(quán):本文版權(quán)歸作者和博客園共有
轉(zhuǎn)載:歡迎轉(zhuǎn)載,但未經(jīng)作者同意,必須保留此段聲明;必須在文章中給出原文連接;否則必究法律責(zé)任
總結(jié)
以上是生活随笔為你收集整理的SpringBoot启动过程解析(简化)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 汉语-词语-依偎:百科
- 下一篇: 怎么用手机查看WiFi密码如何用手机知道