天天看點

Spring Boot 進階-SpringBoot啟動過程源碼分析(二)

作者:架構師面試寶典
Spring Boot 進階-SpringBoot啟動過程源碼分析(二)

上篇文章中,我們介紹了關于Spring Boot在執行run方法之前的一些準備工作,這篇文章我們來詳細分析run()方法中都完成了那些操作。廢話不多說直接上源碼,由于代碼較亂,這裡采用截圖的方式将源碼粘貼出來。

Spring Boot 進階-SpringBoot啟動過程源碼分析(二)

按照上篇結束時候提到的思路,監聽器的注入就是為了監聽到容器的變化事件,根據事件變化來完成具體的容器注入操作。

如何來擷取到監聽器操作

在run方法中有如下的代碼,通過調用getRunListeners方法來擷取到SpringApplicationRunListener監聽器清單。包含了SpringApplicationRunListener清單和ApplicationStartup兩個資訊。

SpringApplicationRunListeners listeners = getRunListeners(args);           

根據分析,我們知道擷取監聽器的操作是通過META-INF/spring.factories中加載的,是以我們可以在對應的配置中找到如下内容

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener           

在org.springframework.boot.context.event.EventPublishingRunListener 類中有一個構造方法。

public EventPublishingRunListener(SpringApplication application, String[] args) {
		// 記錄建立的SpringApplication對象
    this.application = application;
		this.args = args;
    // 建立一個事件廣播機制
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
    // 将容器啟動的時候加載到的事件監聽器全部添加到廣播中。
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}           

那麼我們如何去調用這些監聽器運作呢?

如何調用監聽器?

我們會看到在SpringApplicationRunListener接口中有一個starting方法,這個方法就是用來,運作啟動監聽器的操作方法。

/**
	 * Called immediately when the run method has first started. Can be used for very
	 * early initialization.
	 * @param bootstrapContext the bootstrap context
	 */
	default void starting(ConfigurableBootstrapContext bootstrapContext) {
	}           

在org.springframework.boot.context.event.EventPublishingRunListener監聽器中也調用了對應的方法。并且調用了multicastEvent()方法。将所有的事件操作進行了廣播。

@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
   this.initialMulticaster
         .multicastEvent(
     new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}           

而multicastEvent()方法完成的操作就是周遊了事件監聽器清單。并且執行了onApplicationEvent方法。有興趣的可以深挖一下這個代碼。

把事件監聽器中全部添加到容器中之後,根據源碼邏輯,就是需要準備運作時環境操作。

準備運作時環境

會看到在調用了starting()方法之後,又調用了如下的方法

ConfigurableEnvironment environment = prepareEnvironment(listeners, 
                                                         bootstrapContext, applicationArguments);           

這時候進入到這個方法中會看到執行了如下的一些操作

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
		// Create and configure the environment
    // 擷取到初始化的配置環境
		ConfigurableEnvironment environment = getOrCreateEnvironment();
    // 加載到系統配置
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		ConfigurationPropertySources.attach(environment);
    // 廣播易靜準備好的事件,這裡就是通知容器需要加載一些自定義的配置檔案例如application.yml中的一些配置。
		listeners.environmentPrepared(bootstrapContext, environment);
		DefaultPropertiesPropertySource.moveToEnd(environment);
		Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
				"Environment prefix cannot be set via properties.");
    // 将環境配置綁定到目前的SpringApplication上
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
			environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}           

到這裡,完成了對于配置檔案的載入操作。這裡需要更正一個錯誤,在完成這些操作的時候,并沒有容器相關的參與。而是完成的是容器初始化之前的準備工作。

繼續閱讀