上篇文章中,我們介紹了關于Spring Boot在執行run方法之前的一些準備工作,這篇文章我們來詳細分析run()方法中都完成了那些操作。廢話不多說直接上源碼,由于代碼較亂,這裡采用截圖的方式将源碼粘貼出來。
按照上篇結束時候提到的思路,監聽器的注入就是為了監聽到容器的變化事件,根據事件變化來完成具體的容器注入操作。
如何來擷取到監聽器操作
在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;
}
到這裡,完成了對于配置檔案的載入操作。這裡需要更正一個錯誤,在完成這些操作的時候,并沒有容器相關的參與。而是完成的是容器初始化之前的準備工作。