【深入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-3期】JavaScript深入之記憶體空間詳細圖解
本期的主題是呼叫堆疊,本計劃一共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