SpringBoot事件監聽機制源碼分析(上) SpringBoot源碼(九)


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,ApplicationEnvironmentPreparedEventApplicationContextInitializedEvent等事件,然后相應的監聽器會執行一些SpringBoot啟動過程中的初始化邏輯。那么,監聽這些SpringBoot的生命周期事件的監聽器們是何時被加載實例化的呢?還記得上篇文章在分析SpringApplication的構建過程嗎?沒錯,這些執行初始化邏輯的監聽器們正是在SpringApplication的構建過程中根據ApplicationListener接口去spring.factories配置文件中加載並實例化的。

3.1 為廣播SpringBoot內置生命周期事件做前期准備

3.1.1 加載ApplicationListener監聽器實現類

我們再來回顧下SpringApplication對象是如何構建的? SpringBoot源碼(八)一文中講到在構建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,environmentPreparedcontextPrepared等方法來廣播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,environmentPreparedcontextPrepared等方法中也正是通過調用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

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


公眾號【源碼筆記】,專注於Java后端系列框架的源碼分析。


免責聲明!

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



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