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

打個廣告

我的想寫《springboot源碼解析》這一系列好久了,可是一直角兒心底的知識積累不足,因此一直沒有動筆。html

因此想找一些小夥伴一塊兒寫這一系列,互相糾錯交流學習。java

若是有小夥伴有興趣一塊兒把這一系列的講解寫完的話,加下我微信:13670426148,咱們一塊兒完成,當交流學習。web

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

前言

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

上篇寫完以後才發現以前用的是springboot1.5的版本,可是如今你們都是用springboot2.+,因此我找了個springboot2.0+ 的項目來進行解析,可是第一篇就還沒改變,到時候有空再更新,不過大體的內容仍是差很少的。springboot

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

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: app

因此說獲得的 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的部分,這個內容就有不少了啊。。

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

www.mamicode.com/info-detail…

cloud.tencent.com/developer/a…

相關文章
相關標籤/搜索