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.3 postProcessBeanFactory(beanFactory)

context的子類在配置好BeanFactory后的擴展方法,

這里是AnnotationConfigServletWebServerApplicationContext類
添加了一個后置處理器,一個忽略自動注入接口
beanFactory.addBeanPostProcessor(WebApplicationContextServletContextAwareProcessor) beanFactory.ignoreDependencyInterface(ServletContextAware.
class)

 

5.4 invokeBeanFactoryPostProcessors(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事件

啟動完成

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM