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