走心Springboot源碼解析: 二、SpringApplication.run()方法上部分

打個廣告

個人想寫《springboot源碼解析》這一系列很久了,但是一直角兒心底的知識積累不足,所以一直沒有動筆。

所以想找一些小夥伴一起寫這一系列,互相糾錯交流學習。

如果有小夥伴有興趣一起把這一系列的講解寫完的話,加下我微信:13670426148,我們一起完成,當交流學習。

後期還想寫一系列介紹rpc框架的,不過要再過一陣子了,先把springboot的寫完,堅持一週一更,週末更新

前言

上篇回顧
講了SpringApplication的實例化,其中最主要的是加載Spring的Listener和Initializer,Listener爲10個,Initializer爲6個,詳細查看 走心Springboot源碼解析: 一、SpringApplication的實例化

上篇寫完之後才發現之前用的是springboot1.5的版本,但是現在大家都是用springboot2.+,所以我找了個springboot2.0+ 的項目來進行解析,但是第一篇就還沒改變,到時候有空再更新,不過大致的內容還是差不多的。

這篇主要講run()方法,分成兩部分,這篇先講上半部分,主要聚焦在run()方法中涉及到的  事件-監聽器-廣播 機制,直到創建上下文部分(context = this.createApplicationContext())。

run()方法方法總覽

 public ConfigurableApplicationContext run(String... args) {
        //這是一個計時器,stopWatch.start()開始計時,到後面stopWatch.stop()停止計時。
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        //這個ConfigurableApplicationContext就是我們說的 上下文,後面preContext()的時候會詳細解析,這個是springboot中最重要的一個類了
        ConfigurableApplicationContext context = null;
        //自定義SpringApplication啓動錯誤的回調接口 
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        //此爲設置 System 裏面有一個properties的裏的值 "java.awt.headless" ,設置爲默認值true
        this.configureHeadlessProperty();
        //這個SpringApplicationRunListeners可跟前面的10個Listener不一樣,這裏面封裝了一個廣播。後面會講他的實例化過程
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        //開始進行事件廣播,下面詳細解析
        listeners.starting();
        Collection exceptionReporters;
        try {
            //獲取初始化參數,就是我們運行時的初始化參數,比如“java -jar --port=8080” 其中port就是一個參數了   
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            //environment是運行時環境
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            //打印出springboot的標誌,這個可以自己選擇要打印輸出的文本.
            Banner printedBanner = this.printBanner(environment);
            //創建根上下文,這個方法特別重要,將是下章講解的重點
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            //準備環境,下面先不講了。
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

廣播的封裝

SpringApplicationRunListeners:一個存SpringApplicationRunListener的集合,裏面有些方法,後續都會講到;

SpringApplicationRunListeners : 
    | SpringApplicationRunListener
        |SimpleApplicationEventMulticaster 
SimpleApplicationEventMulticaster
    纔是真正的廣播,SpringApplicationRunListeners只不過是對其的一層封裝

SpringApplicationRunListeners的實例化過程

private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
        //這裏就不贅述了,META-INF/spring.factories下 
        //1. 獲取key爲SpringApplicationRunListener類路徑爲key 對應的 value
        //2. 實例化value對應的類,最後得到的類是EventPublishingRunListener.class,,如圖1.1所示
        return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    }

圖1.1:

所以說得到的 SpringApplicationRunListeners 裏面有一個實例變量,內容是一個 EventPublishingRunListener  組成的 List

  • EventPublishingRunListener繼承於SpringApplicationRunListener。
  • SpringApplicationRunListeners裏面有一個實例變量,是多個EventPublishingRunListener組成的1個List。

SpringApplicationRunListener的結構

從命名我們就可以知道它是一個監聽者,那縱觀整個啓動流程我們會發現,它其實是用來在整個啓動流程中接收不同執行點事件通知的監聽者,SpringApplicationRunListener接口規定了SpringBoot的生命週期,在各個生命週期廣播相應的事件,調用實際的ApplicationListener類。
接下來看SpringApplicationRunListener
SpringApplicationRunListener接口規定了SpringBoot的生命週期

public class SpringApplicationRunListener{
     //剛執行run方法時
    void started();
     //環境建立好時候
    void environmentPrepared(ConfigurableEnvironment environment);
     //上下文建立好的時候
    void contextPrepared(ConfigurableApplicationContext context);
    //上下文載入配置時候
    void contextLoaded(ConfigurableApplicationContext context);
    //上下文刷新完成後,run方法執行完之前
    void finished(ConfigurableApplicationContext context, Throwable exception);
}

它定義了5個步驟:

  • started() -> run方法執行的時候立馬執行;對應事件的類型是ApplicationStartedEvent,通知監聽器,SpringBoot開始執行
  • environmentPrepared() ->  ApplicationContext創建之前並且環境信息準備好的時候調用;對應事件的類型是ApplicationEnvironmentPreparedEvent), 通知監聽器,Environment準備完成
  • contextPrepared -> ApplicationContext創建好並且在source加載之前調用一次;沒有具體的對應事件), 通知監聽器,ApplicationContext已經創建並初始化完成
  • contextLoaded -> ApplicationContext創建並加載之後並在refresh之前調用;對應事件的類型是ApplicationPreparedEvent), 通知監聽器,ApplicationContext已經完成IoC配置價值
  • finished -> run方法結束之前調用;對應事件的類型是ApplicationReadyEvent或ApplicationFailedEvent), 通知監聽器,SpringBoot啓動完成

EventPublishingRunListener的構造器

我們來看EventPublishingRunListener的構造器。還有他的兩個重要的方法:

EventPublishingRunListener類 實現了SpringApplicationRunListener,它具有廣播事件的功能。

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
    //這個是參數傳進來的,也就是SpringApplication.class
    private final SpringApplication application;
    //這個是初始化參數
    private final String[] args;
    //真正的廣 播
    private final SimpleApplicationEventMulticaster initialMulticaster;

    public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
         //新建立廣播器,這個廣播器特別重要,下面繼續解析
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        //application.getListeners()就是獲取10個初始化的Listenter,具體可以參見上一篇瞭解哪10個Listener
        Iterator var3 = application.getListeners().iterator();
        
        while(var3.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var3.next();
            //添加到內部的defaultRetriever裏,詳細代碼可以自己翻進去查看,這裏不拓展,
            //後面需要根據事件類型推測有哪些監聽器需要被觸發,所以就得存把所有的監聽器先存起來,稱作一個註冊表吧
            this.initialMulticaster.addApplicationListener(listener);
        }

    }
}

就是說,EventPublishingRunListener裏面有一個廣播器,結合上面的SpringApplicationRunListener接口聲明的方法,我們可以得到其機制大概是:

  1. run()方法是用來在整個啓動流程中接收不同執行點事件通知的監聽者,喚醒監聽者
  2. 而再進去就是調用EventPublishingRunListener的started()、environmentPrepared()等喚醒的,這裏面就會有根據不同的方法,started(),或者environmentPrepared()等,生成不同的事件。傳遞給廣播器。
  3. 而廣播器SimpleApplicationEventMulticaster initialMulticaster, 就從註冊表中(上文代碼說了註冊表的形成是那段代碼),根據Event的類型,找到那些監聽器是需要被觸發的,執行其 multicastEvent(ApplicationEvent event) 方法,內部是invokeListener(listener, event);

廣播器

multicastEvent() 方法就是廣播方法
SimpleApplicationEventMulticaster廣播出去,廣播出去的事件對象會被SpringApplication中的listeners屬性進行處理。

下面先解析一下廣播方法multicastEvent()執行方法 invokeListener()


public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
        //去註冊表中根據相應的事件,獲取Event對應的Listener。後面會簡略講講獲取的過程
        Iterator var4 = this.getApplicationListeners(event, type).iterator();

        while(var4.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var4.next();
            Executor executor = this.getTaskExecutor();
            if (executor != null) {
                executor.execute(() -> {
                    this.invokeListener(listener, event);
                });
            } else {
                //最後的執行方法是這個,參數是“監聽器和事件”
                this.invokeListener(listener, event);
            }
        }

    }
//執行方法
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
        ErrorHandler errorHandler = this.getErrorHandler();
        if (errorHandler != null) {
            try {
                //看多了源碼你們會發現,spring的源碼很喜歡這麼寫,一般invokeListener的時候不是真的invokeListener的真實過程
                //而是會把真正的邏輯放到 doInvokeListener(do****,這裏就是doInvokeListener)中執行.
                 //1. 而在invokeListener中只是在真正處理方法前做一點數據封裝,
                 //2. 或者異常檢查
                 // 目的:個人感覺主要的目的是把真正處理過程的代碼縮減,使得真正的處理邏輯變得簡潔易懂,不會有多餘的代碼加重理解難度
                this.doInvokeListener(listener, event);
            } catch (Throwable var5) {
                errorHandler.handleError(var5);
            }
        } else {
            this.doInvokeListener(listener, event);
        }
    }    
    
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
        //這裏就很明白了,執行監聽器的方法
            listener.onApplicationEvent(event);
        } catch (ClassCastException var6) {
            String msg = var6.getMessage();
            if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {
                throw var6;
            }

            Log logger = LogFactory.getLog(this.getClass());
            if (logger.isDebugEnabled()) {
                logger.debug("Non-matching event type for listener: " + listener, var6);
            }
        }
    }

所以總結一下 
兩種監聽器和廣播器的關係如下:
SpringApplicationRunListeners、SpringApplicationRunListener、SimpleApplicationEventMulticaster

(1) SpringApplicationRunListeners是SpringApplicationRunListener的封裝。pringApplicationRunListeners中包含多個SpringApplicationRunListener,
是爲了批量執行的封裝,SpringApplicationRunListeners與SpringApplicationRunListener生命週期相同,調用每個週期的各個SpringApplicationRunListener
然後廣播利用SimpleApplicationEventMulticaster進行廣播。

(2)SimpleApplicationEventMulticaster是封裝在SpringApplicationRunListener裏面的廣播器,

通過上面的3個特點可以看出SpringApplicationRunListener。springboot啓動的幾個主要過程的監聽通知都是通過他來進行回調。
流程圖如圖1.2所示


其中就可以看出他們之間的關係了。

開始跑第一個事件started

直接把代碼看到listeners.starting() 這一行

class SpringApplicationRunListeners {
     public void starting() {
        //這裏獲取SpringApplicationRunListeners裏面的List<SpringApplicationRunListener> 
        Iterator var1 = this.listeners.iterator();
        //一般情況下,他只是一個,雖然這裏是個list,所以這個while裏面只會走1次
        while(var1.hasNext()) {
            SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next();
            //這個就進去到SpringApplicationRunListener裏面去了,而此時SpringApplicationRunListener的實現類是EventPublishingRunListener
            listener.starting();
        }
    }
}
/**
**  EventPublishingRunListener實現了SpringApplicationRunListener,所以就得實現裏面的starting(), environmentPrepared()等,貫穿整個springboot啓動流程,而starting()只是他其中最開始的一步,看最上面整體的代碼部分就知道了,後面還有environmentPrepared(), contextPrepared()等
**/
pubic class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
    
    public void starting() {
        //1. 廣播器執行廣播事件方法
        //2. 參數是包裝出來的 ApplicationStartingEvent ,跟下面的environmentPrepared,contextPrepared方法比,他們的事件都類型都不同的
       
        //(1) starting對應的是ApplicationStartingEvent
        //(2) environmentPrepared對應的是 ApplicationEnvironmentPreparedEvent
        //(3) ApplicationContextInitializedEvent對應的是 ApplicationContextInitializedEvent
        this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
    }
    
     public void environmentPrepared(ConfigurableEnvironment environment) {
        this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
    }

    public void contextPrepared(ConfigurableApplicationContext context) {
        this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
    }
    
    //省略****
}


public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {   
    //開始進行廣播方法,因爲此時是staring()方法出發的,所以這裏的envent是在EventPublishingRunListener中new ApplicationContextInitializedEvent,
    public void multicastEvent(ApplicationEvent event) {
        //1. 第二個參數推斷出eventType,,在真正執行廣播
        this.multicastEvent(event, this.resolveDefaultEventType(event));
    }
    
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {   //推斷出類型
        ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
        //從註冊表中根據event類型,獲得所有對應的監聽器,
        //結果獲得的所有監聽器有:
        //(1) LoggingApplicationListener、
        //(2) BackgroundPreinitializer、
        //(3) DelegatingApplicationListener
        //(4) LiquibaseServiceLocatorApplicationListener
        //(5)EnableEncryptablePropertiesBeanFactoryPostProcessor
        //五種類型的對象。這五個對象的onApplicationEvent都會被調用。
        Iterator var4 = this.getApplicationListeners(event, type).iterator();

        while(var4.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var4.next();
            Executor executor = this.getTaskExecutor();
            if (executor != null) {
                executor.execute(() -> {
                    this.invokeListener(listener, event);
                });
            } else {
                //直接看調用方法
                this.invokeListener(listener, event);
            }
        }
    }
//這就是最終的調用點,Listener的onApplicationEvent(ApplicationEvent) 方法。
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
            listener.onApplicationEvent(event);
        } catch (ClassCastException var6) {
            String msg = var6.getMessage();
            if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {
                throw var6;
            }

            Log logger = LogFactory.getLog(this.getClass());
            if (logger.isDebugEnabled()) {
                logger.debug("Non-matching event type for listener: " + listener, var6);
            }
        }
    }
}

那麼這五個監聽器的onApplicationEvent都做了些什麼了,我這裏大概說下,細節的話大家自行去跟源碼。

(1)  LoggingApplicationListener:初始化日誌系統,默認是logback,支持3種,優先級從高到低:logback > log4j > javalog

(2) BackgroundPreinitializer:啓動多個線程執行相應的任務,包括驗證器、消息轉換器等等

(3) DelegatingApplicationListener:此時什麼也沒做

(4) LiquibaseServiceLocatorApplicationListener:此時什麼也沒做

(5)EnableEncryptablePropertiesBeanFactoryPostProcessor:僅僅打印了一句日誌,其他什麼也沒做
對了,補充一點註冊器的代碼
喜歡就看,不喜歡就跳過,

// 返回所有的適合於ApplicationStartedEvent的監聽器集合
protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {
        Object source = event.getSource();
        Class<?> sourceType = source != null ? source.getClass() : null;
        //根據eventType和sourceType組裝一個key即是cacheKey
        AbstractApplicationEventMulticaster.ListenerCacheKey cacheKey = new AbstractApplicationEventMulticaster.ListenerCacheKey(eventType, sourceType);
        //retriever裏面包裝了所有適合的監聽器。
        AbstractApplicationEventMulticaster.ListenerRetriever retriever = (AbstractApplicationEventMulticaster.ListenerRetriever)this.retrieverCache.get(cacheKey);
        //下面是雙通道機制的單例模式寫法,直接看標註註釋的那行代碼即可
        if (retriever != null) {
            return retriever.getApplicationListeners();
        } else if (this.beanClassLoader == null || ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader))) {
            synchronized(this.retrievalMutex) {
                retriever = (AbstractApplicationEventMulticaster.ListenerRetriever)this.retrieverCache.get(cacheKey);
                if (retriever != null) {
                    return retriever.getApplicationListeners();
                } else {
                    //當第一次執行的時候,創建一個 retriever,
                    retriever = new AbstractApplicationEventMulticaster.ListenerRetriever(true);
                    //下面進行詳細解析;
                    //直接在這裏獲取所有適合於ApplicationStartedEvent的監聽器集合,並使用retriever.applicationListeners.add(listener);,添加到retriever中, 
                    Collection<ApplicationListener<?>> listeners = this.retrieveApplicationListeners(eventType, sourceType, retriever);
                    //以cacheKey爲key,存到緩存中。
                    this.retrieverCache.put(cacheKey, retriever);
                    //返回所有的適合於ApplicationStartedEvent的監聽器集合
                    return listeners;
                }
            }
        } else {
            return this.retrieveApplicationListeners(eventType, sourceType, (AbstractApplicationEventMulticaster.ListenerRetriever)null);
        }
    }

//上面方法的解析,直接在這裏獲取所有適合於ApplicationStartedEvent的監聽器集合。
private Collection<ApplicationListener<?>> retrieveApplicationListeners(ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable 
                                                                        AbstractApplicationEventMulticaster.ListenerRetriever retriever) {
        List<ApplicationListener<?>> allListeners = new ArrayList();
        LinkedHashSet listeners;
        LinkedHashSet listenerBeans;
        synchronized(this.retrievalMutex) {
            //this.defaultRetriever.applicationListeners是所有默認的監聽器,就是那10個默認的監聽器,如下圖所示
            listeners = new LinkedHashSet(this.defaultRetriever.applicationListeners);
            listenerBeans = new LinkedHashSet(this.defaultRetriever.applicationListenerBeans);
        }
        
        Iterator var7 = listeners.iterator();

        while(var7.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var7.next();
            //就是在這裏進行判斷的,具體怎麼判斷就不進去了,這個不是重點
            if (this.supportsEvent(listener, eventType, sourceType)) {
                if (retriever != null) {
                    retriever.applicationListeners.add(listener);
                }

                allListeners.add(listener);
            }
        }
    
    //........篇幅有限,這個其實不重要,直接略過即可
}

準備環境 prepareEnvironment

//todo: 這部分可以用“監聽器-事件-廣播” 解釋一下,下週末有時間再進行補充。
prepareEnvironment(listeners, applicationArguments);

加載SpringBoot配置環境(configurableEnvironment),如果是通過web容器發佈,會加載StandardEnvironment。將配置文件(Environment)加入到監聽器對象中(SpringApplicationRunListeners)

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    // Create and configure the environment
    //如果environment不爲空直接返回 || 如果是web環境則直接實例化StandardServletEnvironment類 || 如果不是web環境則直接實例化StandardEnvironment類
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    //配置環境信息
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    //通知所有的監聽者,環境已經準備好了
    listeners.environmentPrepared(environment);
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                deduceEnvironmentClass());
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}


總結

本篇主要講了“監聽器-事件-廣播”的涉及到的幾個類,展示了一個週期 starting()的運行過程。
再總結一下,流程如下:

  1. run()方法是用來在整個啓動流程中接收不同執行點事件通知的監聽者,喚醒監聽者
  2. 而再進去就是調用EventPublishingRunListener的started()、environmentPrepared()等喚醒的,這裏面就會有根據不同的方法,started(),或者environmentPrepared()等,生成不同的事件。傳遞給廣播器。
  3. 而廣播器SimpleApplicationEventMulticaster initialMulticaster, 就從註冊表中(上文代碼說了註冊表的形成是那段代碼),根據Event的類型,找到那些監聽器是需要被觸發的,執行其 multicastEvent(ApplicationEvent event) 方法,內部是invokeListener(listener, event);

下期預告

下期打算講一講, context的部分,這個內容就有很多了啊。。

參考內容: 大神寫的,分析很透徹,站在巨人的肩膀上:

http://www.mamicode.com/info-detail-2735609.html

https://cloud.tencent.com/developer/article/1333056

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章