1. 程式人生 > >【深入SpringBoot 1.3.5 第一章】Boot應用的啟動流程

【深入SpringBoot 1.3.5 第一章】Boot應用的啟動流程

一、 快速建立一個Boot應用

使用maven

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.3.5.RELEASE</version>
</parent>
<dependencies>
    <dependency>
        <groupId>
org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>

應用程式結構

這裡寫圖片描述

App.java作為啟動類

@EnableAutoConfiguration
@ComponentScan(basePackages = {"com.jazz.controller"})
@Configuration
public class App {

    public
static void main(String[] args) { SpringApplication application = new SpringApplication(App.class); application.run(args); } }

* 第一個controller *

@RestController
public class FirstController {

    @RequestMapping("/test_1.service")
    public String queryTeacher() {
        String a="Hello World"
; return a; } }

二、SpringApplication類概要

從上節可以看出:SpringApplication用來啟動整個應用程式。

主要功能

1.根據classspath建立合適的ApplicationContext
2.註冊CommandLinePropertySource生成命令列引數
3.重新整理application context,載入所有bean
4.執行CommandLineRunner bean

使用方式

public static void main(String[] args) throws Exception {
    //例項化--使用當前類作為source(帶有@Configuration的配置類)
    SpringApplication app = new SpringApplication(MyApplication.class);
    // 啟動應用
    app.run(args)
 }

當然還有其他方式,不過類的內部都是根據這種方式轉變而來。

配置(source)

Boot官方文件建議使用帶有@Configuration的java類作為配置,而不是XML或其他配置方式,本系列文章都使用@Configuration。

三、SpringApplication 例項化

通過例項化 SpringApplication application = new SpringApplication(App.class);
主要呼叫了SpringApplication內部的initialize(..)方法,原始碼如下:

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

流程分析
1.設定配置類

2.推斷環境型別(標準或web)

3.例項化spring.factory檔案中ApplicationContextInitializer對應的類

4.例項化spring.factory檔案中ApplicationListener對應的類

5.推斷主類

四、SpringApplication 執行

SpringBoot#run(String… args)

public ConfigurableApplicationContext run(String... args) {
    //計時器,記錄應用啟動消耗時間
    StopWatch stopWatch = new StopWatch();
    ConfigurableApplicationContext context = null;

    //用來設定java.awt.headless 屬性是true 還是false,是J2SE的一種模式用於在缺少顯示屏、鍵盤    
    //或者滑鼠時的系統配置
    configureHeadlessProperty(); 

    //例項化Boot的SpringApplicationRunListeners
    SpringApplicationRunListeners listeners = getRunListeners(args); 

    //執行所有SpringApplicationRunListener的start方法
    listeners.started(); 

    try {
        ApplicationArguments applicationArguments 
                       = new DefaultApplicationArguments( args);

        //5建立並重新整理Spring上下文   
        context = createAndRefreshContext(listeners, applicationArguments);

        //6執行重新整理完上下文的流程
        afterRefresh(context, applicationArguments);

        //7執行所有SpringApplicationRunListener的finish方法。
        listeners.finished(context, null);

        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
        }
        return context;
    }
    catch (Throwable ex) {
        handleRunFailure(context, listeners, ex);
        throw new IllegalStateException(ex);
    }
}

createAndRefreshContext(..)方法

private ConfigurableApplicationContext createAndRefreshContext(
            SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
    ConfigurableApplicationContext context;

    // 5.1 建立並配置環境
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    listeners.environmentPrepared(environment);
    if (isWebEnvironment(environment) && !this.webEnvironment) {
        environment = convertToStandardEnvironment(environment);
    }

    if (this.bannerMode != Banner.Mode.OFF) {
        printBanner(environment);
    }

    //5.2 Create, load, refresh 和run   ApplicationContext
    context = createApplicationContext();
    context.setEnvironment(environment); 

    postProcessApplicationContext(context);
    applyInitializers(context);
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }

    //5.3 新增boot 特殊bean
    context.getBeanFactory().registerSingleton("springApplicationArguments",
                applicationArguments);

    //5.4 載入 sources
    Set<Object> sources = getSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    load(context, sources.toArray(new Object[sources.size()]));
    listeners.contextLoaded(context);

    //5.5 重新整理 context
    refresh(context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        }
        catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
    return context;
}

綜上兩個方法,進行流程分析:
1.程式啟動監聽器
Boot程式開始啟動,執行其開始監聽器。這個方法真正會廣播ApplicationStartedEvent事件給ApplicationListener。
執行以下監聽器

【LoggingApplicationListener】
配置日誌

【ClasspathLoggingApplicationListener】
debug模式則開始日誌資訊

【LiquibaseServiceLocatorApplicationListener】
存在Liquibase類則配置Liquibase

注意:有些監聽器監聽了多個事件,所以下文中還會出現。

2.建立並配置環境
根據命令列引數及其系統屬性、系統環境、Servlet引數等建立環境,並配置profile等。
配置完成的環境物件如下圖:
這裡寫圖片描述

3.執行環境準備好監聽器
廣播ApplicationEnvironmentPreparedEvent事件。 這個事件可以用來修改環境中的屬性源。

【ConfigFileApplicationListener】
這個類會載入spring.factory檔案中的EnvironmentPostProcessor例項到list中,~*這是一個SpringBoot的拓展點。*
然後新增本身,因為它也是EnvironmentPostProcessor
然後依次執行這這些EnvironmentPostProcessor的postProcessEnvironment方法。如下
—SpringApplicationJsonEnvironmentPostProcessor-
把環境中spring.application.json的json值轉化為MapPropertySource,並將此MapPropertySource新增到環境的屬性源列表中
如果在程式啟動之前添加了一個系統屬性json

public static void main(String[] args) {
    System.getProperties().put("spring.application.json", "{ \"name\": \"Web\"}");
    SpringApplication application = new SpringApplication(App.class);  
    application.run(args);
}

那麼,此processor執行完之後,環境的屬性源為:
這裡寫圖片描述

—CloudFoundryVcapEnvironmentPostProcessor-
對CloudFoundry提供支援
—ConfigFileApplicationListener-
首先,向環境的屬性源列表中新增一個Random屬性源,如下
這裡寫圖片描述

然後,從配置檔案中提取相應的鍵值新增到名為applicationConfigurationProperties屬性源中,這個屬性源可以包含多個子屬性源
這裡寫圖片描述

【AnsiOutputApplicationListener】
解析環境中以下屬性
spring.output.ansi.enabled 來決定是否顏色輸出模式,值為列舉AnsiOutput.Enabled (DETECT, ALWAYS, NEVER)
spring.output.ansi.console-available (?測試未成功)是否控制檯輸出

【LoggingApplicationListener】
(?未測試)配置日誌相關,輸出配置相關資訊
這裡寫圖片描述

【BackgroundPreinitializer】
初始化一些資訊

runSafely(new MessageConverterInitializer());
runSafely(new MBeanFactoryInitializer());
runSafely(new ValidationInitializer());

【DelegatingApplicationListener】

這個監聽器監聽所有事件,如果是ApplicationEnvironmentPreparedEvent,那麼獲取環境中key為context.listener.classes的監聽器,後續對這些監聽器廣播相應的事件~拓展點

【FileEncodingApplicationListener】

對spring.mandatoryFileEncoding進行處理

4.列印Banner圖形

感興趣的同學可以實現Banner定製自己的Banner
或者指定banner.location,或者直接新增banner.txt。
~這裡還是比較有趣的、^_^

5.建立ApplicationContext並新增上面的環境
如果沒有在例項變數中新增ApplicationContext,那麼根據是否是網路環境來決定建立新增以下兩種ApplicationContext:

AnnotationConfigEmbeddedWebApplicationContext
AnnotationConfigApplicationContext。

然後把環境設定給ApplicationContext

6.後處理 ApplicationContext
根據是否含有某些例項變數來向ApplicationContext新增:

beanNameGenerator
resourceLoader
resourceLoader

7.執行ApplicationContextInitializer
初始化時新增到例項變數的ApplicationContextInitializer,依次執行:
這裡寫圖片描述
【DelegatingApplicationContextInitializer】
這個類取得環境中“context.initializer.classes”對應的多個ApplicationContextInitializer,用“,”分割,並依次執行~這裡可以進行自己拓展,來修改ApplicationContext

【ContextIdApplicationContextInitializer】
根據
${vcap.application.instance_index:${spring.application.index:${server.port:${PORT:null}}}}
來設定applicationContext的ID

【ConfigurationWarningsApplicationContextInitializer】
為applicationContext新增一個BeanDefinitionRegistryPostProcessor:ConfigurationWarningsPostProcessor,來輸出警告資訊
例如@ComponentScan沒有寫明具體掃描的包名稱

【ServerPortInfoApplicationContextInitializer】
新增監聽器監聽EmbeddedServletContainerInitializedEvent事件,用來設定嵌入的servlet容器埠號(?)
【AutoConfigurationReportLoggingInitializer】
向ApplicationContext新增AutoConfigurationReportListener用來輸出自動配置報告

8.listeners.contextPrepared(context);
把之前用到的廣播器新增到ApplicationContext中,沒有釋出任何事件

9輸出Profile日誌

10註冊命令列引數bean
向ApplicationContext註冊name為springApplicationArguments的Bean

11 載入配置sources
使用ApplicationContext.load載入資源

12 釋出給ApplicationContext監聽器ApplicationPreparedEvent
執行contextLoaded
【ConfigFileApplicationListener】
【LoggingApplicationListener】
【DelegatingApplicationListener】

13 refresh(context);
重新整理上下文,如果是網路環境,那麼進入
AnnotationConfigEmbeddedWebApplicationContext的世界

14 執行完成
主要是執行實現ApplicationRunner和CommandLineRunner介面的類

15 程式完成監聽器
【ConfigFileApplicationListener】
【DelegatingApplicationListener】

後面我們將會從這12步分析~深入理解SpringBoot和Spring的各種原理。
我們發現spring的監聽器貫穿在始終,所以下一節我們將會從監聽器入手

相關推薦

深入SpringBoot 1.3.5 第一Boot應用啟動流程

一、 快速建立一個Boot應用 使用maven <parent> <groupId>org.springframework.boot</groupId> <artifactId>spri

2018-2019-1 20189206 《深入理解計算機系統》第一學習筆記

2018-2019-1 20189206 《深入理解計算機系統》第一週學習總結 教材學習內容總結 第一章 計算機系統漫遊 讀後感 第一章給我的感覺就是將一個大概而具體的過程展現給我們,同時,每個步驟的更加具體的細節部分也是之後每一章的內容。一方面講解了計算機系統的構成、檔案在計算機中的儲存等問題。通過

進階1-3JavaScript深入之記憶體空間詳細圖解

本期的主題是呼叫堆疊,本計劃一共28期,每期重點攻克一個面試重難點,如果你還不瞭解本進階計劃,文末點選檢視全部文章。 如果覺得本系列不錯,歡迎點贊、評論、轉發,您的支援就是我堅持的最大動力。 堆疊的內容和執行順序我就不說了,前面兩篇已經介紹過了。 但是今天補充一個知識點:某些情況下,呼叫堆疊中函式呼

例題:建立一個物件陣列,內放5個學生的資料(學號、成績),用指標指向陣列首元素,輸出第1,3,5個學生的資料。面向物件設計

題目: 建立一個物件陣列,內放5個學生的資料(學號、成績),用指標指向陣列首元素,輸出第1,3,5個學生的資料。 解答:  程式程式碼如下: #include <iostream>

velocity使用——SpringBoot版本由1.5.9變為1.3.5出現問題

1. 背景 Velocity是一個基於Java的模板引擎,通過特定的語法,Velocity可以獲取在java語言中定義的物件,從而實現介面和java程式碼的真正分離,這意味著可以使用velocity替代jsp的開發模式了。這使得前端開發人員可以和 Java 程

maven打包時出現java source 1.3中不支援泛型 請使用 -source 5 或更高版本以啟用泛型錯誤時的解決方法

出現【java source 1.3中不支援泛型 請使用 -source 5 或更高版本以啟用泛型】問題的原因是因為jdk版本使用不對,maven編譯打包時找到的是低版本的jdk,可以在總pom中新增以下語句顯示指明使用版本為1.6的jdk即可解決這個問題 <buil

深入理解計算機系統》第一學習筆記

文件 傳遞 ati 線性 邏輯 double 動態 內容 起源 信息就是位+上下文 源程序:就是一個由0和1組合的位(bit)序列,8位組成一字(byte),每個字節表示某個文本字符。 系統中所有的信息——包括磁盤文件、存儲器中的程序、存儲器中存放的用戶數據以及網絡上傳送的

第一編譯原理基礎

數組 down 生成 符號表 代碼生成 符號 輸入 必須 高性能 編譯器的結構 主要分為2個部分:分析(analysis)部分和綜合(synthesis)部分 分析部分:源程序分解為多個組成要素,並再要素上加上語法結構,創建一個中間表示,相關信息存入符號表。 綜合部分

編程珠璣第一生成隨機數、隨機取樣的問題

當前 rand 可用 生成 奇數 sel 浪費 print 運行時 一、利用隨機數函數生成隨機數 問題1(《編程珠璣》習題12.1後半段): 給定一個rand(),可以產生從0到RAND_MAX的隨機數,其中RAND_MAX很大(常見值:16位int能表示的最大整數3276

補充第一創建虛擬環境,以方便管理

執行文件 rom ruby 開始 greenlet ces bubuko png 版本 一、創建虛擬環境的幾大步驟 virtualenv 是一個創建隔絕的Python環境的工具。virtualenv創建一個包含所有必要的可執行文件的文件夾,用來使用Python工程所需的包。

easyui時間控件設置為可清空——jquery-easyui-1.3.3(這個版本還沒有buttons,網上的好多博文都是1.3.5之後的版本)

沒有 format nbsp strong 分享 dto lose 3.3 AS 效果圖: 更改的源碼jquery.easyui.min.js 11358行: var _858=$("<div class=\"datebox-button\"></di

Eclipse-NO.163.Eclipse.1 -Eclipse springboot 1.x 建立maven工程初始化報錯

Style:Mac Series:Java Since:2018-09-10 End:2018-09-10 Total Hours:1 Degree Of Diffculty:5 Degree Of Mastery:5 Practical Level:5 Desired Goal:5 Arc

51Nod - 1010只包含因子2 3 5的數

題目: K的因子中只包含2 3 5。滿足條件的前10個數是:2,3,4,5,6,8,9,10,12,15。 所有這樣的K組成了一個序列S,現在給出一個數n,求S中 >= 給定數的最小的數。 例如:n = 13,S中 >= 13的最小的數是15,所以輸出15。 Inpu

1!+(1!+3!)+(1!+3!+5!)+...+(1!+3!+5!+7!+9!)的值

今天朋友面試時遇到一道演算法題,題目要求求出1!+(1!+3!)+(1!+3!+5!)+......+(1!+3!+5!+7!+9!)的值。我個人的思路是需要用到斐波那契和階乘來解決這個問題。 這道題可以分為兩步來做,先寫出求階乘的方法,然後我們根據括號就能把上面分組,其實每一組的最大數就是組數*2-1,比如

Mybatis generator生成Service,Controller,新增批量新增資料介面(基於mybatis-generator-1.3.5原始碼修改)

    好久記錄筆記,這段時間做政府的專案,資料錄入系統基本都是通過excel匯入,且資料量大,許多也是單表的錄入,這就有很多可以通用的程式碼,如controller,service層的那一套都是可以程式碼生成,添加了一個數據庫批量新增介面(目前只支援oracle), 程式碼是基於mybatis-gener

Mybatis generator生成Service,Controller,添加批量新增數據接口(基於mybatis-generator-1.3.5源碼修改)

src value new lse 項目上線 uuid err opera auth   好久記錄筆記,這段時間做政府的項目,數據錄入系統基本都是通過excel導入,且數據量大,許多也是單表的錄入,這就有很多可以通用的代碼,如controller,service層的那一套都

第一Spring概述

1.1.1  Spring是什麼 Spring是一個開源的輕量級Java SE(Java 標準版本)/Java EE(Java 企業版本)開發應用框架,其目的是用於簡化企業級應用程式開發。應用程式是由一組相互協作的物件組成。而在傳統應用程式開發中,一個完整的應用是由一組相互

ACMNO.10打印出所有"水仙花數",所謂"水仙花數"是指一個三位數,其各位數字立方和等於該本身。 例如:153是一個水仙花數,因為153=1^3+5^3+3^3。 Output: 153 ???

題目描述 打印出所有"水仙花數",所謂"水仙花數"是指一個三位數,其各位數字立方和等於該本身。 例如:153是一個水仙花數,因為153=1^3+5^3+3^3。 Output: 153 ??? ??? ??? 輸入 無 輸出 所有的水仙花數,從小的開始。

現學現用之windbg的高階玩法(1,3,5,13,14,76,80,81,84,118,119,121,122樓已更新,chm文件整合7篇實戰18個輔助工具)

windbg用的人很少,通常被用作核心偵錯程式。 這對於windbg來說,確實大大限制了windbg的功能發揮。 因為工作的關係,樓主常常需要遠端除錯和到客戶現場排查問題。需要一款順手的偵錯程式。VC由於太大,安裝也麻煩,不能每次都給給客戶安裝一個VC,OD是一款很好的使用者態偵錯程式,但是對pdb支援的不好

springboot 1.3.6中使用actuator預設開啟監控,如何防止資料洩漏

在新增完依賴後 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-actuator</art