SpringBoot 原理 (二): run

SpringApplication 在各種賦值都完成, 初始化部分已完成逊移,下一步是根據(jù)初始化的各種配置,對(duì)應(yīng)用進(jìn)行啟動(dòng),包括bean的加載,配置文件的讀入等等, 調(diào)用的方法為:

springApplication.run(args);

源碼(版本為Spring-boot-1.3.0.M5)如下:

public ConfigurableApplicationContext run(String... args) {
 StopWatch stopWatch = new StopWatch();
 stopWatch.start();
 ConfigurableApplicationContext context = null;
 configureHeadlessProperty();
 SpringApplicationRunListeners listeners = getRunListeners(args);
 listeners.started();
 try {
 context = doRun(listeners, args);
 stopWatch.stop();
 if (this.logStartupInfo) {
 new StartupInfoLogger(this.mainApplicationClass).logStarted(
 getApplicationLog(), stopWatch);
 }
 return context;
 }
 catch (Throwable ex) {
 try {
 listeners.finished(context, ex);
 this.log.error("Application startup failed", ex);
 }
 finally {
 if (context != null) {
 context.close();
 }
 }
 ReflectionUtils.rethrowRuntimeException(ex);
 return context;
 }
 }

1. StopWatch 監(jiān)控

用來記錄任務(wù)的啟動(dòng) 結(jié)束時(shí)間,是一個(gè)非線程的安全的萤悴,如果自己使用要考慮多線程的情況.

2. 配置Headless模式, configureHeadlessProperty

private void configureHeadlessProperty() {
 System.setProperty(
 SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
 System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
 Boolean.toString(this.headless)));
 }

用來設(shè)置java.awt.headless 屬性是true 還是false, java.awt.headless是J2SE的一種模式用于在缺少顯示屏、鍵盤或者鼠標(biāo)時(shí)的系統(tǒng)配置更啄,很多監(jiān)控工具如jconsole 需要將該值設(shè)置為true.

3. SpringApplicationRunListener 對(duì)ApplicationListener事件處理

在初始化的時(shí)候 我們加載了一批 ApplicationListener,包括springboot自啟動(dòng)的 ,也可以有用戶定義ApplicationListener,SpringApplicationRunListener就負(fù)責(zé)來加載ApplicationListener中的業(yè)務(wù).

/**
  * Called immediately when the run method has first started. Can be used for very
  * early initialization.
  */
 void started();

 /**
  * Called once the environment has been prepared, but before the
  * {@link ApplicationContext} has been created.
  * @param environment the environment
  */
 void environmentPrepared(ConfigurableEnvironment environment);

 /**
  * Called once the {@link ApplicationContext} has been created and prepared, but
  * before sources have been loaded.
  * @param context the application context
  */
 void contextPrepared(ConfigurableApplicationContext context);

 /**
  * Called once the application context has been loaded but before it has been
  * refreshed.
  * @param context the application context
  */
 void contextLoaded(ConfigurableApplicationContext context);

 /**
  * Called immediately before the run method finishes.
  * @param context the application context
  * @param exception any run exception or null if run completed successfully.
  */
 void finished(ConfigurableApplicationContext context, Throwable exception);

ApplicationListener可以實(shí)現(xiàn)四中event類型, 分別在不同的時(shí)候加載, ApplicationEnvironmentPreparedEvent,ApplicationPreparedEvent,ApplicationFailedEvent , ApplicationStartedEvent.
在run的方法中分別使用在:

SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.started();
listeners.environmentPrepared(environment);
listeners.contextPrepared(context);
listeners.finished(context, null);

4. doRun 方法實(shí)現(xiàn)

private ConfigurableApplicationContext doRun(SpringApplicationRunListeners listeners,
 String... args) {
 ConfigurableApplicationContext context;
 // Create and configure the environment
 ConfigurableEnvironment environment = getOrCreateEnvironment();
 configureEnvironment(environment, args);
 listeners.environmentPrepared(environment);
 if (this.showBanner) {
 printBanner(environment);
 }

 // Create, load, refresh and run the ApplicationContext
 context = createApplicationContext();
 if (this.registerShutdownHook) {
 try {
 context.registerShutdownHook();
 }
 catch (AccessControlException ex) {
 // Not allowed in some environments.
 }
 }
 context.setEnvironment(environment);
 postProcessApplicationContext(context);
 applyInitializers(context);
 listeners.contextPrepared(context);
 if (this.logStartupInfo) {
 logStartupInfo(context.getParent() == null);
 }

 // Add boot specific singleton beans
 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
 context.getBeanFactory().registerSingleton("springApplicationArguments",
 applicationArguments);

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

 // Refresh the context
 refresh(context);
 afterRefresh(context, applicationArguments);
 listeners.finished(context, null);
 return context;
 }```
#####a. 創(chuàng)建和配置環(huán)境相關(guān) 
 i. 創(chuàng)建 environment 
        首先根據(jù)初始化的配置創(chuàng)建environment的類型, 后面或者property     和profile都是通過這個(gè)類來獲取的,然后configureEnvironment 就是用來Property和Profiled的.
     
ii. 啟動(dòng)說監(jiān)聽器的environmentPrepared方法,會(huì)調(diào)用所有監(jiān)聽器中監(jiān)聽ApplicationEnvironmentPreparedEvent的方法,具體邏輯在上一步中已經(jīng)描述了.

 iii. printBanner 
          打印啟動(dòng)的banner,工業(yè)化的項(xiàng)目中可以對(duì)此進(jìn)行定制,使用Banner類即可.

#####b. 創(chuàng)建 加載 刷新 運(yùn)行ApplicationContext 
 i.創(chuàng)建context
   ```java
protected ConfigurableApplicationContext createApplicationContext() {
 Class<?> contextClass = this.applicationContextClass;
 if (contextClass == null) {
 try {
 contextClass = Class
 .forName(this.webEnvironment ? DEFAULT_WEB_CONTEXT_CLASS
 : DEFAULT_CONTEXT_CLASS);
 }
 catch (ClassNotFoundException ex) {
 throw new IllegalStateException(
 "Unable create a default ApplicationContext, "
 + "please specify an ApplicationContextClass", ex);
 }
 }
 return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
 }   

根據(jù)是否是webEnvironment 創(chuàng)建不同的context,是創(chuàng)建的是AnnotationConfigEmbedded,WebApplicationContext, 不是創(chuàng)建的默認(rèn)的 AnnotationConfig, ApplicationContext.

ii.注冊(cè)關(guān)閉的鉤子稚疹,主要實(shí)現(xiàn)當(dāng)JVM關(guān)閉時(shí)候,做一些關(guān)閉處理居灯,如銷毀bean祭务,生命周期關(guān)閉等, 實(shí)現(xiàn)方式是

 Runtime.getRuntime().addShutdownHook(this.shutdownHook);

JVM關(guān)閉時(shí)會(huì)自動(dòng)調(diào)用.

iii. initializer 初始化 日志啟動(dòng)等其他操作.

c. 配置spring boot特有的單例bean 如commandline相關(guān)
d.加載資源怪嫌,就是在初始化過程中source包含的類
e.context 重新刷新

這個(gè)主要都走spring 框架的AbstractApplicationContext#refresh义锥,是對(duì)context中的各種資源進(jìn)行加載, 由于不是boot特有,這里不仔細(xì)描述

f.監(jiān)聽器實(shí)現(xiàn)所有finished操作

主要的實(shí)現(xiàn)ApplicationReadyEvent 或者 ApplicationFailedEvent.

至此岩灭,springboot啟動(dòng)過程分析完成了.

5. 后記

本身主要研究分析了boot本身的代碼的情況拌倍,并沒有對(duì)spring的代碼 和 每個(gè)監(jiān)聽器功能進(jìn)行深入分析后面可開專門文章對(duì)此進(jìn)行補(bǔ)充 ,方便不熟悉spring的同學(xué)理解.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市噪径,隨后出現(xiàn)的幾起案子柱恤,更是在濱河造成了極大的恐慌,老刑警劉巖找爱,帶你破解...
    沈念sama閱讀 222,464評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梗顺,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡车摄,警方通過查閱死者的電腦和手機(jī)寺谤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吮播,“玉大人变屁,你說我怎么就攤上這事∫夂荩” “怎么了粟关?”我有些...
    開封第一講書人閱讀 169,078評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)环戈。 經(jīng)常有香客問我誊役,道長(zhǎng),這世上最難降的妖魔是什么谷市? 我笑而不...
    開封第一講書人閱讀 59,979評(píng)論 1 299
  • 正文 為了忘掉前任蛔垢,我火速辦了婚禮,結(jié)果婚禮上迫悠,老公的妹妹穿的比我還像新娘鹏漆。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,001評(píng)論 6 398
  • 文/花漫 我一把揭開白布艺玲。 她就那樣靜靜地躺著括蝠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪饭聚。 梳的紋絲不亂的頭發(fā)上忌警,一...
    開封第一講書人閱讀 52,584評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音秒梳,去河邊找鬼法绵。 笑死,一個(gè)胖子當(dāng)著我的面吹牛酪碘,可吹牛的內(nèi)容都是我干的朋譬。 我是一名探鬼主播,決...
    沈念sama閱讀 41,085評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼兴垦,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼徙赢!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起探越,我...
    開封第一講書人閱讀 40,023評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤狡赐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后钦幔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體枕屉,經(jīng)...
    沈念sama閱讀 46,555評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,626評(píng)論 3 342
  • 正文 我和宋清朗相戀三年节槐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了搀庶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,769評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡铜异,死狀恐怖哥倔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情揍庄,我是刑警寧澤咆蒿,帶...
    沈念sama閱讀 36,439評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站蚂子,受9級(jí)特大地震影響沃测,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜食茎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,115評(píng)論 3 335
  • 文/蒙蒙 一蒂破、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧别渔,春花似錦附迷、人聲如沸惧互。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽喊儡。三九已至,卻和暖如春稻据,著一層夾襖步出監(jiān)牢的瞬間艾猜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評(píng)論 1 274
  • 我被黑心中介騙來泰國打工捻悯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留匆赃,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,191評(píng)論 3 378
  • 正文 我出身青樓秋度,卻偏偏與公主長(zhǎng)得像炸庞,于是被迫代替她去往敵國和親钱床。 傳聞我的和親對(duì)象是個(gè)殘疾皇子荚斯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,781評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容