1. 程式人生 > >Spring Boot SpringApplication啟動類(二)

Spring Boot SpringApplication啟動類(二)

目錄

  • 前言
  • 1、起源
  • 2、SpringApplication 執行階段
    • 2.1 SpringApplicationRunListeners 結構
      • 2.1.1 SpringApplicationRunListener 事件和監聽機制
      • 2.1.2 SimpleApplicationEventMulticaster 廣播器
    • 2.2 ApplicationArguments 載入啟動引數
    • 2.3 ConfigurableEnvironment 載入外部化配置
    • 2.4 ConfigurableApplicationContext 建立 Spring 應用上下文
  • 3、總結

前言

        最近在學習Spring Boot相關的課程,過程中以筆記的形式記錄下來,方便以後回憶,同時也在這裡和大家探討探討,文章中有漏的或者有補充的、錯誤的都希望大家能夠及時提出來,本人在此先謝謝了!

開始之前呢,希望大家帶著幾個問題去學習:
1、Spring Boot SpringApplication 是什麼?
2、整體流程或結構是怎樣的?
3、重點內容或者核心部分是什麼?
4、怎麼實現的?
5、是怎麼和 Spring 關聯起來的?
這是對自我的提問,我認為帶著問題去學習,是一種更好的學習方式,有利於加深理解。好了,接下來進入主題。

1、起源

        上篇文章我們講了 SpringApplication 的準備階段,在這個階段,完成了執行時所需要準備的資源,如:initializerslisteners等。而這篇文章我們就來講講 SpringApplication 的執行階段,在這個階段,它是如何啟動 Spring 應用上下文的,且如何與 Spring 事件結合起來,形成完整的 SpringApplication生命週期的。

注:本篇文章所用到的 Spring Boot版本是 2.1.6.BUILD-SNAPSHOT

2、SpringApplication 執行階段

        上篇文章我們講了 SpringApplication 的構造方法,這裡我們就來講講 SpringApplication 的核心,也就是run方法,程式碼如下:

public class SpringApplication {

    ...
        
    public ConfigurableApplicationContext run(String... args) {
        // 這是 Spring 的一個計時器,計算程式碼的執行時間(ms級別)
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        
        // 這倆變數在後面賦值處進行說明
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        
        // 用來設定java.awt.headless屬性值
        configureHeadlessProperty();
        
        // 該物件屬於組合模式的實現,核心是內部關聯的 SpringApplicationRunListener 集合,SpringApplicationRunListener 是 Spring Boot 的執行時監聽器
        SpringApplicationRunListeners listeners = getRunListeners(args);
        // 會在不同的階段呼叫對應的方法,這裡表示啟動run方法被呼叫
        listeners.starting();
        
        try {
        
            // 用來獲取 SpringApplication.run(args)傳入的引數
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            
            // 獲取 properties 配置檔案
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            
            // 設定 spring.beaninfo.ignore 的屬性值,判斷是否跳過搜尋BeanInfo類
            configureIgnoreBeanInfo(environment);
            
            // 這裡是專案啟動時,控制檯列印的 Banner
            Banner printedBanner = printBanner(environment);
            
            // 這裡就是建立 Spring 應用上下文
            context = createApplicationContext();
            
            // 獲取 spring.factories 中key為 SpringBootExceptionReporter 的類名集合
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
                    
            // 這裡是準備 Spring 應用上下文
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            
            // 這裡是啟動 Spring 應用上下文,底層呼叫的是 ApplicationContext 的 refresh() 方法,到這裡就正式進入了 Spring 的生命週期,同時,SpringBoot的自動裝配特性也隨之啟動
            refreshContext(context);
            
            // 裡面是空的,猜測應該是交由開發人員自行擴充套件
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            
            // 這裡列印啟動資訊
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
            
            // ApplicationContext 啟動時,呼叫該方法
            listeners.started(context);
            
            // 專案啟動後,做的一些操作,開發人員可自行擴充套件
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }
    
        try {
        
            // ApplicationContext 啟動完成時,呼叫該方法
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }
    
    ...
}

上面就是整個過程的概覽,可以看到,在執行階段執行的操作比較多,雖然看起來雜亂無章,但其實還是有規律可循的。比如,執行的 SpringApplicationRunListeners 中的階段方法,剛啟動階段的 starting 、已啟動階段的 started 、啟動完成階段的 running 等。還有對應的 Spring 應用上下文的建立、準備、啟動操作等。接下來,就對裡面的幾個核心物件進行討論。

2.1 SpringApplicationRunListeners 結構

我們先來看看 SpringApplicationRunListeners 物件,從程式碼可以看出該物件是由 getRunListeners 方法建立的:

private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger,
            getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}

可以看到,通過傳入的 getSpringFactoriesInstances 方法的返回值,執行 SpringApplicationRunListeners 的構造方法,進行物件的建立。接著看 getSpringFactoriesInstances 方法:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = getClassLoader();
    // Use names and ensure unique to protect against duplicates
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

看到這大家應該比較熟悉了,通過前面幾篇文章的討論我們知道,該方法通過 SpringFactoriesLoader.loadFactoryNames 返回所有 classpass 下的 spring.factories 檔案中 key 為 SpringApplicationRunListener 的實現類集合。如 Spring Boot 的內建實現:

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

最後,就是將該集合傳入 SpringApplicationRunListeners 的構造方法:

class SpringApplicationRunListeners {

    ...

    private final List<SpringApplicationRunListener> listeners;

    SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
        this.log = log;
        this.listeners = new ArrayList<>(listeners);
    }

    public void starting() {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.starting();
        }
    }

    ...

}

裡面是將集合賦值到 listeners 屬性,可以看到 SpringApplicationRunListeners 屬於組合模式的實現,核心其實是內部關聯的 SpringApplicationRunListener 物件集合,當外部呼叫該階段方法時,就會迭代執行集合中 SpringApplicationRunListener 對應的方法。所以接下來我們就來討論 SpringApplicationRunListener

2.1.1 SpringApplicationRunListener 事件和監聽機制

SpringApplicationRunListener 負責在 SpringBoot 的不同階段廣播相應的事件,然後呼叫實際的 ApplicationListener 類,在該類的 onApplicationEvent 方法中,根據不同的 Spring Boot 事件執行相應操作。整個過程大概如此,接下來進行詳細討論,先來看看 SpringApplicationRunListener 定義:

public interface SpringApplicationRunListener {

    // 在run()方法開始執行時被呼叫,表示應用剛剛啟動,對應的 Spring Boot 事件為 ApplicationStartingEvent
    void starting();

    // ConfigurableEnvironment 構建完成時呼叫,對應的 Spring Boot 事件為 ApplicationEnvironmentPreparedEvent
    void environmentPrepared(ConfigurableEnvironment environment);

    // ApplicationContext 構建完成時呼叫,對應的 Spring Boot 事件為 ApplicationContextInitializedEvent
    void contextPrepared(ConfigurableApplicationContext context);

    // ApplicationContext 完成載入但還未啟動時呼叫,對應的 Spring Boot 事件為 ApplicationPreparedEvent
    void contextLoaded(ConfigurableApplicationContext context);

    // ApplicationContext 已啟動,但 callRunners 還未執行時呼叫,對應的 Spring Boot 事件為 ApplicationStartedEvent
    void started(ConfigurableApplicationContext context);

    // ApplicationContext 啟動完畢被呼叫,對應的 Spring Boot 事件為 ApplicationReadyEvent
    void running(ConfigurableApplicationContext context);

    // 應用出錯時被呼叫,對應的 Spring Boot 事件為 ApplicationFailedEvent
    void failed(ConfigurableApplicationContext context, Throwable exception);

}

我們來看看它的實現類,也就是上面載入的 spring.factories 檔案中的 EventPublishingRunListener 類,該類也是 Spring Boot 內建的唯一實現類,具體廣播事件的操作在該類中進行,程式碼如下:

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

    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();
        for (ApplicationListener<?> listener : application.getListeners()) {
            this.initialMulticaster.addApplicationListener(listener);
        }
    }

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

    ...

}

可以看到,通過構造方法建立 EventPublishingRunListener 例項的過程中,呼叫了 getListeners 方法,將 SpringApplication 中所有 ApplicationListener 監聽器關聯到了 initialMulticaster 屬性中。沒錯,這裡的 ApplicationListener 監聽器就是上篇文章中在 SpringApplication 準備階段從 spring.factories 檔案載入的 key 為 ApplicationListener 的實現類集合,該實現類集合全部重寫了 onApplicationEvent 方法。

2.1.2 SimpleApplicationEventMulticaster 廣播器

這裡又引出了另一個類, 也就是 SimpleApplicationEventMulticaster ,該類是 Spring 的事件廣播器,也就是通過它來廣播各種事件。接著,當外部迭代的執行到 EventPublishingRunListenerstarting 方法時,會通過 SimpleApplicationEventMulticastermulticastEvent 方法進行事件的廣播,這裡廣播的是 ApplicationStartingEvent 事件,我們進入 multicastEvent 方法:

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

    ...
    
    @Override
    public void multicastEvent(ApplicationEvent event) {
        multicastEvent(event, resolveDefaultEventType(event));
    }

    @Override
    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        Executor executor = getTaskExecutor();
        for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            }
            else {
                invokeListener(listener, event);
            }
        }
    }
}

通過 getApplicationListeners 方法,根據事件型別返回從上面關聯的 ApplicationListener 集合中篩選出匹配的 ApplicationListener 集合,根據 Spring Boot 版本的不同,在這個階段獲取到的監聽器也有可能不同,如 2.1.6.BUILD-SNAPSHOT 版本返回的是:

然後依次遍歷這些監聽器,同步或非同步的呼叫 invokeListener 方法:

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
        ErrorHandler errorHandler = getErrorHandler();
    if (errorHandler != null) {
        try {
            doInvokeListener(listener, event);
        }
        catch (Throwable err) {
            errorHandler.handleError(err);
        }
    }
    else {
        doInvokeListener(listener, event);
    }
}

...

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
            listener.onApplicationEvent(event);
        }
        catch (ClassCastException ex) {
            String msg = ex.getMessage();
            if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
                // Possibly a lambda-defined listener which we could not resolve the generic event type for
                // -> let's suppress the exception and just log a debug message.
                Log logger = LogFactory.getLog(getClass());
                if (logger.isTraceEnabled()) {
                    logger.trace("Non-matching event type for listener: " + listener, ex);
                }
            }
            else {
                throw ex;
            }
        }
    }

可以看到,最終呼叫的是 doInvokeListener 方法,在該方法中執行了 ApplicationListeneronApplicationEvent 方法,入參為廣播的事件物件。我們就拿其中一個的監聽器來看看 onApplicationEvent 中的實現,如 BackgroundPreinitializer 類:

public class BackgroundPreinitializer implements ApplicationListener<SpringApplicationEvent> {

    ...
    
    @Override
    public void onApplicationEvent(SpringApplicationEvent event) {
        if (!Boolean.getBoolean(IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME)
                && event instanceof ApplicationStartingEvent && preinitializationStarted.compareAndSet(false, true)) {
            performPreinitialization();
        }
        if ((event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent)
                && preinitializationStarted.get()) {
            try {
                preinitializationComplete.await();
            }
            catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
        }
    }
    
    ...
}

在該方法中,通過 instanceof 判斷事件的型別,從而進行相應的操作。該監聽器主要的操作是新建一個後臺執行緒去執行那些耗時的初始化工作,包括驗證器、訊息轉換器等。LoggingApplicationListener 監聽器則是對 Spring Boot 的日誌系統做一些初始化的前置操作。另外兩個監聽器在該階段無任何操作。

至此,SpringBoot 事件機制的整體流程大概如此,我們簡要回顧一下幾個核心元件:

  • SpringApplicationRunListeners:首先,在 run 方法的執行過程中,通過該類在 SpringBoot 不同的階段呼叫不同的階段方法,如在剛啟動階段呼叫的 starting 方法。

  • SpringApplicationRunListener:而 SpringApplicationRunListeners 屬於組合模式的實現,它裡面關聯了 SpringApplicationRunListener 實現類集合,當外部呼叫階段方法時,會迭代執行該集合中的階段方法。實現類集合是 spring.factories 檔案中定義好的類。這裡是一個擴充套件點,詳細的後面述說。

  • EventPublishingRunListener:該類是 Spring Boot 內建的 SpringApplicationRunListener 唯一實現類,所以,當外部呼叫各階段的方法時,真正執行的是該類中的方法。

  • SimpleApplicationEventMulticaster:在階段方法中,會通過 SpringSimpleApplicationEventMulticaster 事件廣播器,廣播各個階段對應的事件,如這裡的 starting 方法廣播的事件是 ApplicationStartingEvent

  • ApplicationListener:最後 ApplicationListener 的實現類也就是 Spring Boot 監聽器會監聽到廣播的事件,根據不同的事件,進行相應的操作。這裡的 Spring Boot 監聽器是也是在 spring.factories 中定義好的,這裡我們也可自行擴充套件。

到這裡 Spring Boot 事件監聽機制差不多就結束了,值得注意的是 Spring Boot 監聽器實現的是 SpringApplicationListener 類,事件類最終繼承的也是 SpringApplicationEvent 類,所以,Spring Boot 的事件和監聽機制都基於 Spring 而實現的。

2.2 ApplicationArguments 載入啟動引數

        當執行完 listeners.starting 方法後,接著進入構造 ApplicationArguments 階段:

public class SpringApplication {

    ...
    
    public ConfigurableApplicationContext run(String... args) {
        
        ...
        
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            
            ...
        }
    }
    
    ...
}

該類是用於簡化 Spring Boot 應用啟動引數的封裝介面,我們啟動專案時輸入的命令引數會封裝在該類中。一種是通過 IDEA 輸入的引數,如下:

另一種是 springboot jar包執行時傳遞的引數:cmd中執行java -jar xxx.jar name=張三 pwa=123

然後,可以通過 @Autowired 注入 ApplicationArguments 的方式進行使用:

public class Test {

    @Autowired
    private ApplicationArguments applicationArguments;

    public void getArgs() {
        // 獲取 args 中的所有 non option 引數
        applicationArguments.getNonOptionArgs();

        // 獲取 args 中所有的 option 引數的 name
        applicationArguments.getOptionNames();

        // 獲取傳遞給應用程式的原始未處理引數
        applicationArguments.getSourceArgs();

        // 獲取 args 中指定 name 的 option 引數的值
        applicationArguments.getOptionValues("nmae");

        // 判斷從引數中解析的 option 引數是否包含指定名稱的選項
        applicationArguments.containsOption("name");
    }
}

2.3 ConfigurableEnvironment 載入外部化配置

        接著進入構造 ConfigurableEnvironment 的階段,該類是用來處理我們外部化配置的,通俗點講就是處理 properties 的檔案,提供對配置檔案的基礎操作。此外,Spring Boot 存在預設配置檔案,也就是 application.properties 。這裡我們進行大致的瞭解即可,後面會有專門的文章討論這塊內容。我們進入建立該類的 prepareEnvironment 方法:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
    // Create and configure the environment
    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;
}

這裡通過 getOrCreateEnvironment 方法返回具體的 Environment

private ConfigurableEnvironment getOrCreateEnvironment() {
        if (this.environment != null) {
            return this.environment;
        }
        switch (this.webApplicationType) {
        case SERVLET:
            return new StandardServletEnvironment();
        case REACTIVE:
            return new StandardReactiveWebEnvironment();
        default:
            return new StandardEnvironment();
        }
    }

可以看到,這裡通過 webApplicationType 屬性來判斷當前應用的型別,有 ServletReactive 、 非Web 3種類型,該屬性也是在上篇文章中 SpringApplication 準備階段確定的,這裡我們通常都是 Servlet 型別,返回的是 StandardServletEnvironment 例項。

之後,還呼叫了 SpringApplicationRunListenersenvironmentPrepared 階段方法,表示 ConfigurableEnvironment 構建完成,同時向 Spring Boot 監聽器釋出 ApplicationEnvironmentPreparedEvent 事件。監聽該事件的監聽器有:

2.4 ConfigurableApplicationContext 建立 Spring 應用上下文

        這裡通過 createApplicationContext 方法建立 Spring 應用上下文,實際上 Spring 的應用上下文才是驅動 Spring Boot 的核心引擎:

public class SpringApplication {

    ...

    public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
            + "annotation.AnnotationConfigApplicationContext";

    public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
            + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

    public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
            + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";

    ...

    protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                    break;
                case REACTIVE:
                    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                    break;
                default:
                    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                }
            }
            catch (ClassNotFoundException ex) {
                throw new IllegalStateException(
                        "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",
                        ex);
            }
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }
    
    ...
}

這裡也是通過 webApplicationType 屬性來確定應用型別從而建立 String 上下文,上篇文章說到該屬性值是在 Spring Boot 準備階段推匯出來的。這裡我們的應用型別是 Servlet ,所以建立的是 AnnotationConfigServletWebServerApplicationContext 物件。建立完 Spring 應用上下文之後,執行 prepareContext 方法進入準備上下文階段:

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    context.setEnvironment(environment);
    postProcessApplicationContext(context);
    applyInitializers(context);
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof DefaultListableBeanFactory) {
        ((DefaultListableBeanFactory) beanFactory)
                .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    // Load the sources
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    load(context, sources.toArray(new Object[0]));
    listeners.contextLoaded(context);
}

我們來看看主要做了哪些操作:

  1. 設定了 Spring 應用上下文的 ApplicationArguments,上面說過是處理外部化配置的,具體型別為 StandardServletEnvironment

  2. Spring 應用上下文後置處理,主要是覆蓋當前 Spring 應用上下文預設所關聯的 ResourceLoaderClassLoader

  3. 執行 Spring 的初始化器,上篇文章說過在 Spring Boot 準備階段初始化了一批在 spring.factories 檔案中定義好的 ApplicationContextInitializer ,這裡就是執行它們的 initialize 方法,同時這裡也是一個擴充套件點,後面詳細討論。

  4. 執行 SpringApplicationRunListenerscontextPrepared 階段方法,表示 ApplicationContext 準備完成,同時向 Spring Boot 監聽器釋出 ApplicationContextInitializedEvent 事件 。

  5. springApplicationArgumentsspringBootBanner 註冊為 Bean

  6. 載入 Spring 應用上下文的配置源,也是在上篇文章 Spring Boot 準備階段獲取的 primarySourcessourcesprimarySources 來源於 SpringApplication 構造器引數,sources 則來源於自定義配置的 setSources 方法。

  7. 最後執行 SpringApplicationRunListenerscontextLoaded 階段方法,表示 ApplicationContext 完成載入但還未啟動,同時向 Spring Boot 監聽器釋出 ApplicationPreparedEvent 事件 。

接下來就是真正啟動階段,執行的是 refreshContext 方法:

private void refreshContext(ConfigurableApplicationContext context) {
    refresh(context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        }
        catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
}
protected void refresh(ApplicationContext applicationContext) {
        Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    ((AbstractApplicationContext) applicationContext).refresh();
}

可以看到,底層呼叫的是 AbstractApplicationContextrefresh 方法,到這裡 Spring 應用正式啟動,Spring Boot 核心特性也隨之啟動,如自動裝配。隨後執行 SpringApplicationRunListenersstarted 階段方法,表示 ApplicationContext 已啟動,同時向 Spring Boot 監聽器釋出 ApplicationStartedEvent 事件 。但還未啟動完成,後面還有一個 callRunners 方法,一般來講,裡面執行一些我們自定義的操作。之後 Spring 應用才算啟動完成,隨後呼叫 running 方法,釋出 ApplicationReadyEvent 事件。至此,SpringApplication 執行階段結束。

3、總結

        最後來對 SpringApplication 執行階段做一個總結。這個階段核心還是以啟動 Spring 應用上下文為主,同時根據應用型別來初始化不同的上下文物件,但這些物件的基類都是 SpringConfigurableApplicationContext 類。且在啟動的各個階段中,使用 SpringApplicationRunListeners 進行事件廣播,回撥 Spring Boot 的監聽器。同時還初始化了 ApplicationArgumentsConfigurableEnvironment 等幾個元件。下篇文章我們就來討論 Spring Boot 的外部化配置部分,來看看為什麼外部的各個元件,如 RedisDubbo 等在 properties 檔案中進行相應配置後,就可以正常使用。

以上就是本章的內容,如過文章中有錯誤或者需要補充的請及時提出,本人感激不盡。



參考:

《Spring Boot 程式設計思想》
https://www.cnblogs.com/youzhibing/p/9603119.html
https://www.jianshu.com/p/b86a7c8b3442
https://www.cnblogs.com/duanxz/p/11243271.html
https://www.jianshu.com/p/7a674c59