天天看點

SpringBoot事件監聽機制源碼分析(上) SpringBoot源碼(九)1 溫故而知新2 引言3 SpringBoot廣播内置生命周期事件流程分析3.1 為廣播SpringBoot内置生命周期事件做前期準備3.1.1 加載ApplicationListener監聽器實作類3.1.2 加載SPI擴充類EventPublishingRunListener3.2 廣播SpringBoot的内置生命周期事件4 SpringBoot的内置生命周期事件總結5 小結

SpringBoot中文注釋項目Github位址: https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE 本篇接 SpringApplication對象是如何建構的? SpringBoot源碼(八)

1 溫故而知新

溫故而知新,我們來簡單回顧一下上篇的内容,上一篇我們分析了SpringApplication對象的建構過程及SpringBoot自己實作的一套SPI機制,現将關鍵步驟再濃縮總結下:

  1. SpringApplication

    對象的構造過程其實就是給

    SpringApplication

    類的6個成員變量指派;
  2. SpringBoot通過以下步驟實作自己的SPI機制:
  • 1)首先擷取線程上下文類加載器;
  • 2)然後利用上下文類加載器從

    spring.factories

    配置檔案中加載所有的SPI擴充實作類并放入緩存中;
  • 3)根據SPI接口從緩存中取出相應的SPI擴充實作類;
  • 4)執行個體化從緩存中取出的SPI擴充實作類并傳回。

2 引言

在SpringBoot啟動過程中,每個不同的啟動階段會分别廣播不同的内置生命周期事件,然後相應的監聽器會監聽這些事件來執行一些初始化邏輯工作比如

ConfigFileApplicationListener

會監聽

onApplicationEnvironmentPreparedEvent

事件來加載配置檔案

application.properties

的環境變量等。

是以本篇内容将來分析下SpringBoot的事件監聽機制的源碼。

3 SpringBoot廣播内置生命周期事件流程分析

為了探究SpringBoot廣播内置生命周期事件流程,我們再來回顧一下SpringBoot的啟動流程代碼:

// SpringApplication.java

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    // 【0】建立一個SpringApplicationRunListeners對象用于發射SpringBoot啟動過程中的生命周期事件
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 【1】》》》》》發射【ApplicationStartingEvent】事件,标志SpringApplication開始啟動
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                args);
        // 【2】》》》》》發射【ApplicationEnvironmentPreparedEvent】事件,此時會去加載application.properties等配置檔案的環境變量,同時也有标志環境變量已經準備好的意思
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                applicationArguments);
        configureIgnoreBeanInfo(environment);
        Banner printedBanner = printBanner(environment);
        context = createApplicationContext();
        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context); 
        // 【3】》》》》》發射【ApplicationContextInitializedEvent】事件,标志context容器被建立且已準備好
        // 【4】》》》》》發射【ApplicationPreparedEvent】事件,标志Context容器已經準備完成
        prepareContext(context, environment, listeners, applicationArguments,
                printedBanner);
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                    .logStarted(getApplicationLog(), stopWatch);
        }
        // 【5】》》》》》發射【ApplicationStartedEvent】事件,标志spring容器已經重新整理,此時所有的bean執行個體都已經加載完畢
        listeners.started(context);
        callRunners(context, applicationArguments);
    }
    // 【6】》》》》》發射【ApplicationFailedEvent】事件,标志SpringBoot啟動失敗
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }
    try {
        // 【7】》》》》》發射【ApplicationReadyEvent】事件,标志SpringApplication已經正在運作即已經成功啟動,可以接收服務請求了。
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}           

可以看到SpringBoot在啟動過程中首先會先建立一個

SpringApplicationRunListeners

對象用于發射SpringBoot啟動過程中的各種生命周期事件,比如發射

ApplicationStartingEvent

,

ApplicationEnvironmentPreparedEvent

ApplicationContextInitializedEvent

等事件,然後相應的監聽器會執行一些SpringBoot啟動過程中的初始化邏輯。那麼,監聽這些SpringBoot的生命周期事件的監聽器們是何時被加載執行個體化的呢?還記得上篇文章在分析

SpringApplication

的建構過程嗎?沒錯,這些執行初始化邏輯的監聽器們正是在

SpringApplication

的建構過程中根據

ApplicationListener

接口去

spring.factories

配置檔案中加載并執行個體化的。

3.1 為廣播SpringBoot内置生命周期事件做前期準備

3.1.1 加載ApplicationListener監聽器實作類

我們再來回顧下

一文中講到在建構

SpringApplication

對象時的

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

這句代碼。

這句代碼做的事情就是從

spring.factories

中加載出

ApplicationListener

事件監聽接口的SPI擴充實作類然後添加到

SpringApplication

對象的

listeners

集合中,用于後續監聽SpringBoot啟動過程中的事件,來執行一些初始化邏輯工作。

SpringBoot啟動時的具體監聽器們都實作了

ApplicationListener

接口,其在

spring.factories

部配置設定置如下:

不過在調試時,會從所有的spring.factories配置檔案中加載監聽器,最終加載了10個監聽器。如下圖:

3.1.2 加載SPI擴充類EventPublishingRunListener

前面講到,在SpringBoot的啟動過程中首先會先建立一個

SpringApplicationRunListeners

對象用于發射SpringBoot啟動過程中的生命周期事件,即我們現在來看下

SpringApplicationRunListeners listeners = getRunListeners(args);

這句代碼:

// SpringApplication.java

private SpringApplicationRunListeners getRunListeners(String[] args) {
    // 構造一個由SpringApplication.class和String[].class組成的types
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    // 1) 根據SpringApplicationRunListener接口去spring.factories配置檔案中加載其SPI擴充實作類
    // 2) 建構一個SpringApplicationRunListeners對象并傳回
    return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
            SpringApplicationRunListener.class, types, this, args));
}
           

我們将重點放到`getSpringFactoriesInstances(

SpringApplicationRunListener.class, types, this, args)`這句代碼,`getSpringFactoriesInstances`這個方法我們已經很熟悉,在上一篇分析SpringBoot的SPI機制時已經詳細分析過這個方法。可以看到SpringBoot此時又是根據`SpringApplicationRunListener`這個SPI接口去`spring.factories`中加載相應的SPI擴充實作類,我們直接去`spring.factories`中看看`SpringApplicationRunListener`有哪些SPI實作類:
                   

由上圖可以看到,

SpringApplicationRunListener

隻有

EventPublishingRunListener

這個SPI實作類

EventPublishingRunListener

這個哥們在SpringBoot的啟動過程中尤其重要,由其在SpringBoot啟動過程的不同階段發射不同的SpringBoot的生命周期事件,即

SpringApplicationRunListeners

對象沒有承擔廣播事件的職責,而最終是委托

EventPublishingRunListener

這個哥們來廣播事件的。

因為從

spring.factories

中加載

EventPublishingRunListener

類後還會執行個體化該類,那麼我們再跟進

EventPublishingRunListener

的源碼,看看其是如何承擔發射SpringBoot生命周期事件這一職責的?

// EventPublishingRunListener.java

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

    private final SpringApplication application;

    private final String[] args;
    /**
     * 擁有一個SimpleApplicationEventMulticaster事件廣播器來廣播事件
     */
    private final SimpleApplicationEventMulticaster initialMulticaster;

    public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        // 建立一個事件廣播器SimpleApplicationEventMulticaster對象
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        // 周遊在構造SpringApplication對象時從spring.factories配置檔案中擷取的事件監聽器
        for (ApplicationListener<?> listener : application.getListeners()) {
            // 将從spring.factories配置檔案中擷取的事件監聽器們添加到事件廣播器initialMulticaster對象的相關集合中
            this.initialMulticaster.addApplicationListener(listener);
        }
    }

    @Override
    public int getOrder() {
        return 0;
    }
    // 》》》》》發射【ApplicationStartingEvent】事件
    @Override
    public void starting() {
        this.initialMulticaster.multicastEvent(
                new ApplicationStartingEvent(this.application, this.args));
    }
    // 》》》》》發射【ApplicationEnvironmentPreparedEvent】事件
    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
                this.application, this.args, environment));
    }
    // 》》》》》發射【ApplicationContextInitializedEvent】事件
    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(
                this.application, this.args, context));
    }
    // 》》》》》發射【ApplicationPreparedEvent】事件
    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        for (ApplicationListener<?> listener : this.application.getListeners()) {
            if (listener instanceof ApplicationContextAware) {
                ((ApplicationContextAware) listener).setApplicationContext(context);
            }
            context.addApplicationListener(listener);
        }
        this.initialMulticaster.multicastEvent(
                new ApplicationPreparedEvent(this.application, this.args, context));
    }
    // 》》》》》發射【ApplicationStartedEvent】事件
    @Override
    public void started(ConfigurableApplicationContext context) {
        context.publishEvent(
                new ApplicationStartedEvent(this.application, this.args, context));
    }
    // 》》》》》發射【ApplicationReadyEvent】事件
    @Override
    public void running(ConfigurableApplicationContext context) {
        context.publishEvent(
                new ApplicationReadyEvent(this.application, this.args, context));
    }
    // 》》》》》發射【ApplicationFailedEvent】事件
    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        ApplicationFailedEvent event = new ApplicationFailedEvent(this.application,
                this.args, context, exception);
        if (context != null && context.isActive()) {
            // Listeners have been registered to the application context so we should
            // use it at this point if we can
            context.publishEvent(event);
        }
        else {
            // An inactive context may not have a multicaster so we use our multicaster to
            // call all of the context's listeners instead
            if (context instanceof AbstractApplicationContext) {
                for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
                        .getApplicationListeners()) {
                    this.initialMulticaster.addApplicationListener(listener);
                }
            }
            this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
            this.initialMulticaster.multicastEvent(event);
        }
    }
    
    // ...省略非關鍵代碼
}
           

可以看到

EventPublishingRunListener

類實作了

SpringApplicationRunListener

接口,

SpringApplicationRunListener

接口定義了SpringBoot啟動時發射生命周期事件的接口方法,而

EventPublishingRunListener

類正是通過實作

SpringApplicationRunListener

接口的

starting

environmentPrepared

contextPrepared

等方法來廣播SpringBoot不同的生命周期事件,我們直接看下

SpringApplicationRunListener

接口源碼好了:

// SpringApplicationRunListener.java

public interface SpringApplicationRunListener {

    void starting();

    void environmentPrepared(ConfigurableEnvironment environment);

    void contextPrepared(ConfigurableApplicationContext context);

    void contextLoaded(ConfigurableApplicationContext context);

    void started(ConfigurableApplicationContext context);

    void running(ConfigurableApplicationContext context);

    void failed(ConfigurableApplicationContext context, Throwable exception);
}           

我們再接着分析

EventPublishingRunListener

這個類,可以看到其有一個重要的成員屬性

initialMulticaster

,該成員屬性是

SimpleApplicationEventMulticaster

類對象,該類正是承擔了廣播SpringBoot啟動時生命周期事件的職責,即

EventPublishingRunListener

SimpleApplicationEventMulticaster

這個哥們來廣播事件的。 從

EventPublishingRunListener

的源碼中也可以看到在

starting

environmentPrepared

contextPrepared

等方法中也正是通過調用

SimpleApplicationEventMulticaster

類對象的

multicastEvent

方法來廣播事件的。

思考 SpringBoot啟動過程中發射事件時事件廣播者是層層委托職責的,起初由

SpringApplicationRunListeners

對象承擔,然後

SpringApplicationRunListeners

對象将廣播事件職責委托給

EventPublishingRunListener

對象,最終

EventPublishingRunListener

對象将廣播事件的職責委托給

SimpleApplicationEventMulticaster

對象。為什麼要層層委托這麼做呢? 這個值得大家思考。

前面講到從

spring.factories

EventPublishingRunListener

類後會執行個體化,而執行個體化必然會通過

EventPublishingRunListener

的構造函數來進行執行個體化,是以我們接下來分析下

EventPublishingRunListener

的構造函數源碼:

// EventPublishingRunListener.java

public EventPublishingRunListener(SpringApplication application, String[] args) {
    this.application = application;
    this.args = args;
    // 建立一個事件廣播器SimpleApplicationEventMulticaster對象
    this.initialMulticaster = new SimpleApplicationEventMulticaster();
    // 周遊在構造SpringApplication對象時從spring.factories配置檔案中擷取的事件監聽器
    for (ApplicationListener<?> listener : application.getListeners()) {
        // 将從spring.factories配置檔案中擷取的事件監聽器們添加到事件廣播器initialMulticaster對象的相關集合中
        this.initialMulticaster.addApplicationListener(listener);
    }
}           

可以看到在

EventPublishingRunListener

的構造函數中有一個

for

循環會周遊之前從

spring.factories

中加載的監聽器們,然後添加到集合中緩存起來,用于以後廣播各種事件時直接從這個集合中取出來即可,而不用再去

spring.factories

中加載,提高效率。

3.2 廣播SpringBoot的内置生命周期事件

spring.factories

配置檔案中加載并執行個體化

EventPublishingRunListener

對象後,那麼在在SpringBoot的啟動過程中會發射一系列SpringBoot内置的生命周期事件,我們再來回顧下SpringBoot啟動過程中的源碼:

// SpringApplication.java

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    // 【0】建立一個SpringApplicationRunListeners對象用于發射SpringBoot啟動過程中的生命周期事件
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 【1】》》》》》發射【ApplicationStartingEvent】事件,标志SpringApplication開始啟動
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                args);
        // 【2】》》》》》發射【ApplicationEnvironmentPreparedEvent】事件,此時會去加載application.properties等配置檔案的環境變量,同時也有标志環境變量已經準備好的意思
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                applicationArguments);
        configureIgnoreBeanInfo(environment);
        Banner printedBanner = printBanner(environment);
        context = createApplicationContext();
        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context); 
        // 【3】》》》》》發射【ApplicationContextInitializedEvent】事件,标志context容器被建立且已準備好
        // 【4】》》》》》發射【ApplicationPreparedEvent】事件,标志Context容器已經準備完成
        prepareContext(context, environment, listeners, applicationArguments,
                printedBanner);
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                    .logStarted(getApplicationLog(), stopWatch);
        }
        // 【5】》》》》》發射【ApplicationStartedEvent】事件,标志spring容器已經重新整理,此時所有的bean執行個體都已經加載完畢
        listeners.started(context);
        callRunners(context, applicationArguments);
    }
    // 【6】》》》》》發射【ApplicationFailedEvent】事件,标志SpringBoot啟動失敗
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }
    try {
        // 【7】》》》》》發射【ApplicationReadyEvent】事件,标志SpringApplication已經正在運作即已經成功啟動,可以接收服務請求了。
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}           

可以看到在SpringBoot的啟動過程中總共會發射7種不同類型的生命周期事件,來标志SpringBoot的不同啟動階段,同時,這些生命周期事件的監聽器們也會執行一些啟動過程中的初始化邏輯,關于這些監聽器的初始化邏輯将在下一篇内容中會分析。以下是SpringBoot啟動過程中要發射的事件類型,其中

ApplicationFailedEvent

在SpringBoot啟動過程中遇到異常才會發射:

  1. ApplicationStartingEvent

  2. ApplicationEnvironmentPreparedEvent

  3. ApplicationContextInitializedEvent

  4. ApplicationPreparedEvent

  5. ApplicationStartedEvent

  6. ApplicationFailedEvent

  7. ApplicationReadyEvent

我們以

listeners.starting();

這句代碼為例,看看

EventPublishingRunListener

對象發射事件的源碼:

// SpringApplicationRunListeners.java

public void starting() {
    // 周遊listeners集合,這裡實質取出的就是剛才從spring.factories中取出的SPI實作類EventPublishingRunListener
    // 而EventPublishingRunListener對象承擔了SpringBoot啟動過程中負責廣播不同的生命周期事件
    for (SpringApplicationRunListener listener : this.listeners) {
            // 調用EventPublishingRunListener的starting方法來廣播ApplicationStartingEvent事件
        listener.starting();
    }
}           

繼續跟進

listener.starting();

的源碼:

EventPublishingRunListener.java

// 》》》》》發射【ApplicationStartingEvent】事件
public void starting() {
    // EventPublishingRunListener對象将釋出ApplicationStartingEvent這件事情委托給了initialMulticaster對象
    // 調用initialMulticaster的multicastEvent方法來發射ApplicationStartingEvent事件
    this.initialMulticaster.multicastEvent(
            new ApplicationStartingEvent(this.application, this.args));
}           

可以看到,

EventPublishingRunListener

對象将釋出

ApplicationStartingEvent

這件事情委托給了

SimpleApplicationEventMulticaster

對象

initialMulticaster

,而

initialMulticaster

對象最終會調用其

multicastEvent

方法來發射

ApplicationStartingEvent

事件。關于

SimpleApplicationEventMulticaster

類如何廣播事件,筆者已經在

Spring是如何實作事件監聽機制的? Spring源碼(二)

這篇文章已經詳細分析,這裡不再贅述。

關于SpringBoot啟動過程中發射其他生命周期事件的源碼這裡不再分析

4 SpringBoot的内置生命周期事件總結

好了,前面已經分析了SpringBoot啟動過程中要發射的各種生命周期事件,下面列一個表格總結下:

5 小結

SpringBoot啟動過程中廣播生命周期事件的源碼分析就到此結束了,下一篇會繼續介紹監聽這些生命周期事件的監聽器們。我們再回顧本篇内容總結下關鍵點:

SpringBoot啟動過程中會發射7種類型的生命周期事件,标志不同的啟動階段,然後相應的監聽器會監聽這些事件來執行一些初始化邏輯工作;

【源碼筆記】Github源碼分析項目上線啦!!!下面是筆記的Github位址:

https://github.com/yuanmabiji/Java-SourceCode-Blogs

點贊和轉發是對筆者最大的激勵哦!