1. 程式人生 > >SpringBoot深入(二)--SpringBoot啟動過程分析

SpringBoot深入(二)--SpringBoot啟動過程分析

前言

前一篇分析了SpringBoot如何啟動以及內建web容器,這篇我們一起看一下SpringBoot的整個啟動過程,廢話不多說,正文開始。

正文

一、SpringBoot的啟動類是**application,以註解@SpringBootApplication註明。

@SpringBootApplication
public class CmsApplication {

    public static void main(String[] args) {
        SpringApplication.run(CmsApplication.class, args);

    }
}

SpringBootApplication註解是@Configuration,@EnableAutoConfiguration,@ComponentScan三個註解的整合,分別表示Springbean的配置bean,開啟自動配置spring的上下文,元件掃描的路徑,這也是為什麼*application.java需要放在根路徑的原因,這樣@ComponentScan掃描的才是整個專案。

二、該啟動類預設只有一個main方法,呼叫的是SpringApplication.run方法,下面我們來看一下SpringApplication這個類。

public static ConfigurableApplicationContext run(Object source
, String... args) { return run(new Object[]{source}, args); } ... public static ConfigurableApplicationContext run(Object[] sources, String[] args) { return (new SpringApplication(sources)).run(args);//sources為具體的CmsApplication.class類 } ...

抽出其中兩個直接呼叫的run方法,可以看出靜態方法SpringApplication.run最終建立了一個SpringApplication,並執行其中run方法。

檢視起構造方法:

public SpringApplication(Object... sources) {
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.initialize(sources);
    }
...

構造方法設定了基礎值後呼叫initialize方法進行初始化,如下:

private void initialize(Object[] sources) {
        if (sources != null && sources.length > 0) {
            this.sources.addAll(Arrays.asList(sources));
        }
        this.webEnvironment = this.deduceWebEnvironment();
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }
...

初始化方法主要做了幾步:
1.將source放入SpringApplication的sources屬性中管理,sources是一個LinkedHashSet(),這意味著我們可以同時建立多個自定義不重複的Application,但是目前只有一個。
2.判斷是否是web程式(javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext都必須在類載入器中存在),並設定到webEnvironment屬性中 。
3.從spring.factories中找出ApplicationContextInitializer並設定到初始化器initializers。
4.從spring.factories中找出ApplicationListener,並例項化後設置到SpringApplication的監聽器listeners屬性中。這個過程就是找出所有的應用程式事件監聽器。
5.找出的main方法的類(這裡是CmsApplication),並返回Class物件。

預設情況下,initialize方法從spring.factories檔案中找出的key為ApplicationContextInitializer的類有:

  • org.springframework.boot.context.config.DelegatingApplicationContextInitializer
  • org.springframework.boot.context.ContextIdApplicationContextInitializer
  • org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer
  • org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer
  • org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

key為ApplicationListener的有:

  • org.springframework.boot.context.config.ConfigFileApplicationListener
  • org.springframework.boot.context.config.AnsiOutputApplicationListener
  • org.springframework.boot.logging.LoggingApplicationListener
  • org.springframework.boot.logging.ClasspathLoggingApplicationListener
  • org.springframework.boot.autoconfigure.BackgroundPreinitializer
  • org.springframework.boot.context.config.DelegatingApplicationListener
  • org.springframework.boot.builder.ParentContextCloserApplicationListener
  • org.springframework.boot.context.FileEncodingApplicationListener
  • org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

三、SpringApplication構造和初始化完成後,便是執行其run方法

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();// 構造一個任務執行觀察器
        stopWatch.start();// 開始執行,記錄開始時間
        ConfigurableApplicationContext context = null;
        FailureAnalyzers analyzers = null;
        this.configureHeadlessProperty();
        // 獲取SpringApplicationRunListeners,內部只有一個EventPublishingRunListener
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        // 封裝成SpringApplicationEvent事件然後廣播出去給SpringApplication中的listeners所監聽,啟動監聽
        listeners.starting();

        try {
            // 構造一個應用程式引數持有類
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 載入配置環境
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            Banner printedBanner = this.printBanner(environment);
            // 建立Spring容器(使用BeanUtils.instantiate)
            context = this.createApplicationContext();
            // 若容器建立失敗,分析輸出失敗原因
            new FailureAnalyzers(context);
            // 設定容器配置環境,監聽等
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            // 重新整理容器
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            // 廣播出ApplicationReadyEvent事件給相應的監聽器執行
            listeners.finished(context, (Throwable)null);
            stopWatch.stop();// 執行結束,記錄執行時間
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            return context;// 返回Spring容器
        } catch (Throwable var9) {
            this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);
            throw new IllegalStateException(var9);
        }
    }

run方法過程分析如上,該方法幾個關鍵步驟如下:

1.建立了應用的監聽器SpringApplicationRunListeners並開始監聽

2.載入SpringBoot配置環境(ConfigurableEnvironment),如果是通過web容器釋出,會載入StandardEnvironment,其最終也是繼承了ConfigurableEnvironment,類圖如下
這裡寫圖片描述
可以看出,*Environment最終都實現了PropertyResolver介面,我們平時通過environment物件獲取配置檔案中指定Key對應的value方法時,就是呼叫了propertyResolver介面的getProperty方法。

3.配置環境(Environment)加入到監聽器物件中(SpringApplicationRunListeners)

4.建立Spring容器:ConfigurableApplicationContext(應用配置上下文),我們可以看一下建立方法

protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                contextClass = Class.forName(this.webEnvironment ? "org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext" : "org.springframework.context.annotation.AnnotationConfigApplicationContext");
            } catch (ClassNotFoundException var3) {
                throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
            }
        }

        return (ConfigurableApplicationContext)BeanUtils.instantiate(contextClass);
    }

方法會先獲取顯式設定的應用上下文(applicationContextClass),如果不存在,再載入預設的環境配置(通過是否是web environment判斷),預設選擇AnnotationConfigApplicationContext註解上下文(通過掃描所有註解類來載入bean),最後通過BeanUtils例項化上下文物件,並返回,ConfigurableApplicationContext類圖如下這裡寫圖片描述
主要看其繼承的兩個方向:
LifeCycle:生命週期類,定義了start啟動、stop結束、isRunning是否執行中等生命週期空值方法
ApplicationContext:應用上下文類,其主要繼承了beanFactory(bean的工廠類)。

5.回到run方法內,設定容器prepareContext方法,將listeners、environment、applicationArguments、banner等重要元件與上下文物件關聯

6.重新整理容器,refresh()方法,初始化方法如下:

public void refresh() throws BeansException, IllegalStateException {
        Object var1 = this.startupShutdownMonitor;
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }

refresh()方法做了很多核心工作比如BeanFactory的設定,BeanFactoryPostProcessor介面的執行、BeanPostProcessor介面的執行、自動化配置類的解析、spring.factories的載入、bean的例項化、條件註解的解析、國際化的初始化等等。這部分內容會在之後的文章中分析。

7.廣播出ApplicationReadyEvent,執行結束返回ConfigurableApplicationContext。

至此,SpringBoot啟動完成,回顧整體流程,Springboot的啟動,主要建立了配置環境(environment)、事件監聽(listeners)、應用上下文(applicationContext),並基於以上條件,在容器中開始例項化我們需要的Bean。

相關推薦

SpringBoot深入--SpringBoot啟動過程分析

前言 前一篇分析了SpringBoot如何啟動以及內建web容器,這篇我們一起看一下SpringBoot的整個啟動過程,廢話不多說,正文開始。 正文 一、SpringBoot的啟動類是**application,以註解@SpringBootAppl

SpringBoot學習SpringBoot一些很實用的功能

一、定製Banner springboot在啟動的時候,會有下面這個圖片 然後這個圖案是可以自定義的,在src/main/resource下新建一個banner.txt檔案,然後去http://patorjk.com/software/taag/下自定義

SpringBoot深入--SpringBoot內建web容器及配置

前言 在學會基本運用SpringBoot同時,想必搭過SSH、SSM等開發框架的小夥伴都有疑惑,SpringBoot在spring的基礎上做了些什麼,使得使用SpringBoot搭建開發框架能如此簡單,便捷,快速。本系列文章記錄博主網羅部落格、分析原始

Exynos4412 核心移植—— 核心啟動過程分析

核心啟動所用函式如下:         與移植U-Boot 的過程相似,在移植Linux 之前,先了解它的啟動過程。Linux 的過程可以分為兩部分:架構/開發板相關的引導過程、後續的通用啟動過程。對於uImage、zImage ,它們首先進行自解壓得到vmlinux

Exynos4412 Uboot 移植—— Uboot 啟動流程分析

    uboot啟動流程分析如下: 第一階段: a -- 設定cpu工作模式為SVC模式 b -- 關閉中斷,mmu,cache v -- 關看門狗 d -- 初始化記憶體,串列埠 e -- 設定棧 f -- 程式碼自搬移 g -- 清bss h -- 跳c 第二階段 a

SpringBoot系列啟動原理解析

要想了解開一輛車首先你要知道,這輛車的駕駛座在哪,以及它是如何啟動的。接下來我們就一起了解一下SpringBoot的啟動原理,之前說過這是SpringBoot入口那麼下面來看幾個註解------ @SpringBootApplication 我們不難發現@SpringBoot

SpringBootSpringBoot 啟動流程

SpringBoot 啟動流程: 首先我們看看一切的起源——SpringBoot啟動類: 執行main方法,然後呼叫SpringApplication.run()方法,這樣我們的專案就啟動了。是不是很神奇。 那麼我們來看看run方法,他到底為我們做了什麼,一起慢慢剝

Spring Boot參考教程SpringBoot特性

provide 初始化 spa using 一個 ora https ann war 2. Spring Boot特性 1. Starter pom(起步依賴) Spring提供了一系列的starterpom來簡化Maven的依賴加載,具體可參考官方文檔13.5 Star

企業分布式微服務雲SpringCloud SpringBoot mybatis Spring Boot屬性配置文件詳解

public profile 功能 tps with oot oschina 基本 ava 相信很多人選擇Spring Boot主要是考慮到它既能兼顧Spring的強大功能,還能實現快速開發的便捷。我們在Spring Boot使用過程中,最直觀的感受就是沒有了原來自己整合S

springboot熱部署——springboot熱部署與發布

延遲 標簽頁 tools settings 通過 選擇 auto tex pid 一、實現的方式概述      註意以下的熱部署方式在IDEA是默認沒有打開自動編譯的,手動編譯需要快捷鍵(Ctrl+Shift+F9),自動編譯的修改配置如下:(註意刷新不要太快,會有1-

資料新增非同步解析重新整理大資料量redis —— SpringBoot之CommandLineRunner介面和ApplicationRunner介面

在spring boot應用中,我們可以在程式啟動之前執行任何任務。為了達到這個目的,我們需要使用CommandLineRunner或ApplicationRunner介面建立bean,spring boot會自動監測到它們。這兩個介面都有一個run()方法,在實現介面時需要覆蓋該方法,並使用@

成長記錄貼之springboot+shiro {完成一個完整的許可權控制,詳細步驟}

       近一個月比較忙,公司接了一個新專案,領導要求用shiro進行安全管理,而且全公司只有我一個java,從專案搭建到具體介面全是一個人再弄。不過剛好前段時間大概學習了一下shiro的使用,還算順利。       &n

SpringBoot使用--配置

配置是Spring Framework的核心元素,必須要有東西告訴Spring如何執行應用程式。 在嚮應用程式加入Spring Boot時,有個名為spring-boot-autoconfigure的JAR檔案,其中包含了很多配置類。每個配置類都在應用程式的Classpath裡,都有機會

SpringBoot開發詳解--SpringBoot的配置檔案以及註解

轉載自:https://blog.csdn.net/qq_31001665/article/details/69938750 一、Spring Boot註解 通過上一篇文章,我們已經快速構建了一個spring boot的專案,那spring boot專案和我們之前使用的springMVC專案

springboot入門

第二種啟動方式: package wyh.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.Rest

SpringBoot學習--構建RESTFUL API並用JdbcTemplate進行儲存

RESTFUL API 首先,REST是所有Web應用都應該遵守的架構設計指導原則。面向資源是REST最明顯的特徵,對於同一個資源的一組不同的操作。在這裡我們是對一個User物件進行操作。增刪改查。REST要求,必須通過統一的介面來對資源執行各種操作。對於每個資源只能執行一組有限的操作。

SpringBoot教程

上一節我們已經運行了SpringBoot的HelloWorld,很簡單,不用像Spring那麼多配置,我們分析一下 分析 我們看一下pom.xml 1.父專案 <parent> <groupId>org.springfr

企業級 SpringBoot 教程 Spring Boot配置檔案詳解

springboot採納了建立生產就緒Spring應用程式的觀點。 Spring Boot優先於配置的慣例,旨在讓您儘快啟動和執行。在一般情況下,我們不需要做太多的配置就能夠讓spring boot正常執行。在一些特殊的情況下,我們需要做修改一些配置,或者需要有自己的配置屬性

springboot+mybatis plus快速構建專案

註明:我將通過一個Demo來闡述MyBatis-Plus的強大 我的環境 JDK1.8 maven3.5.4 開發工具idea springboot2.0.5 mybatis-plus3.0.3 mysql5.7 上面是我的環境版本。 對應的資料庫指令碼

springBoot相關

Spring Boot 三大特性: 元件自動裝配:Web MVC 、Web Flux 、JDBC等  嵌入式Web容器:Tomcat、Jetty以及Undertow 生產準備特性:指標、健康檢查、外部化配置等   元件自動裝配: 啟用:@Enable