Spring Boot啟動過程

Spring Boot啟動

  1. 關鍵類、方法:ConfigurableApplicationContext SpringApplication#run。
    new SpringApplication(primarySources).run(args);

類方法說明

  1. SpringApplication類
    可以被用來從Java主方法中引導、啟動一個Spring應用的類。默認,類會執行下面步驟來引導你的應用:
    1. 創建一個合適的ApplicationContext實例(依賴你的類路徑)。
    2. 注冊一個CommandLinePropertySource來暴露命令行參數為Spring屬性。
    3. 刷新應用上下位,加載所有的單例bean。
    4. 觸發任意的CommandLinerRunner bean。
      在多數情況下,靜態run(Class, String[])可以直接從你的main方法調用,來引導你的應用。
      對于更高級的配置,SpringApplication實例可以在被運行前創建和自定義:
SpringApplication application = new SpringApplication(MyApplication.class);
//...在這里自定義應用設置
application.run(args);

SpringApplication可以從各種源中讀取bean。通常建議@Configuration來引導你的應用,然而,你也可能從下面方式中設置getSources():
1. 由AnnotatedBeanDefinitionReader加載的全限定類名
2. 由XmlBeanDefinitionReader加載的XML資源位置,或者由GroovyBeanDefinitionReader加載的groovy腳本
3. ClassPathBeanDefinitionScanner掃描的包名
配置屬性也被綁定到SpringApplication。這讓動態設置SpringApplication屬性成為可能,像額外的源("spring.main.sources",一個CSV列表)標志來表示web環境("spring.main.web-application-type=none")或者關閉橫幅的("spring.main.banner-mode=off")。

  1. 方法SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources)
    創建一個新的SpringApplication實例。應用上下文會從指定主要源加載bean(看SpringApplication類級別文檔。實例可以在調用run(String...)之前自定義)。
  2. 方法Collection<T> getSpringFactoriesInstance(Class<T> type, Class<?>[] paramenterTypes, Object... args)
  3. ResourceLoader接口
    加載資源的策略接口(例如,類路徑或者文件系統資源)。org.springframework.context.ApplicationContext被要求提供這個功能,延申擴展org.springframework.core.io.support.ResourcePatternResolver支持。
    DefaultResourceLoader是一個獨立實現,在ApplicationContext外是可用的,也被ResourceEditor使用。
    Resource類型的bean屬性和Resource數組可以從字符串中填充,當運行在ApplicationContext中時,使用特殊的上下文資源加載策略。
  4. Resource getResource(String location):為給定的資源位置返回一個Resource處理。該處理應該總是可以重復使用的資源描述符,允許多次Resource#getInputStream()調用。
    1. 必須支持全限定URL,例如,"file:C:/test.dat"
    2. 必須支持類路徑偽限定URL,例如, "classpath:test.dat"
    3. 應該支持相對路徑,例如, "WEB-INF/test.dat"。(這會被具體實現,一般由ApplicationContext實現提供)
      注意,一個Resource處理不表明一個存在的資源;你需要調用Resource#exists去檢查存在性。
  5. 類WebApplicationType
    web應用可能類型的枚舉:NONE、SERVLET、REACTIVE。NONE:該類型應用不應該運行為web應用,不應該啟動內置web服務器;SERVLET: 該類型應用應該運行為基于servlet的web應用,應該啟動內置servlet web服務器;REACTIVE:該類型應用應該運行為響應式web應用,應該啟動內置響應式web服務器。
  6. 方法WebApplicationType deduceFromClassPath
    從類路徑上推斷Web應用類型,SERVLET的標志類```{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"}。默認返回SERVLET。
  7. 接口 ApplicationContextInitializer
    初始化Spring ConfigurableApplicationContext的回調接口,優于被ConfigurableApplicationConetxt#refresh()刷新。
    一般用在web應用上,就是要求應用上下文的一些編程方式的初始化。例如,根據ConfigurableApplicationContext#getEnvironment() 上下文的環境來注冊屬性元或者激活配置。查看ContextLoader和FrameworkServlet,支持聲明一個contextInitializerClasses上下文參數和初始化參數。
    ApplicationContextInitializer處理器鼓勵去檢查是否Spring的org.springframework.core.Ordered 接口已經被實現,或者是否org.springframework.core.annotation.Order注解是否出現,來排序實例。
  8. 方法void initialize(C applicationContext):初始化給定的應用上下文
  9. 類SpringFactoriesLoader
    用在框架內部的普通目的工廠加載機制。
    SpringFactoriesLoader#loadFactories加載并且實例化#FACTORIES_RESOURCE_LOCATION文件中給定類型的工廠,它們可能出現在類路徑中的多個JAR文件中。spring.factories文件必須用Properties格式,在這種格式種,建時全限定的接口或者抽象類名稱,值時逗號分隔的實現類名列表。例如:
example.MyService=example.MyServiceImpl1,example.MyServiceImpl2
# example.MyService時接口的名稱,MyServiceImpl1和MyServiceImpl2是兩個實現
  1. 方法List<String> loadFactoryNames(Class<?> factoryType, ClassLoader classLoader)
    加載#FACTORIES_RESOURCE_LOCATION中給定的工廠實現的全限定類名,使用給定的類加載器。
  2. 方法loadSpringFactories
    FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories",從該文件中,加載資源,解析屬性,并且添加到緩存中。
  3. 方法createSpringFactoriesInstances
    利用反射,加載、實例化上一步工廠配置中指定的名稱對應的類,并添加大鏈表中。
  4. instantiateFactory
    使用反射,創建工廠實現類的實例。
  5. 方法 setInitializers
    設置將會應用到Spring ApplicationContext的ApplicationCOntextInitializer
  6. 接口ApplicationListener
    函數式接口。由應用事件監聽器實現的接口。基于標準的java.util.EventListener接口,用于觀察者設計模式。
    在Spring 3.0時,一個ApplicationListener通常可以聲明感興趣的事件類型。當用Spring ApplicationContext注冊時,事件會相應地被過濾,監聽器只在匹配的事件對象被調用。
  7. onApplicationEvent
    處理一個應用事件
  8. EventListener
    所有事件監聽器接口必須繼承的標記接口
  9. ApplicationEvent
    被所有應用事件繼承的類。抽象是因為直接發布通用事件是沒有意義的。
  10. ApplicationEventMulticaster
    被可以管理許多ApplicationListener對象,并且可以發布事件給它們的對象實現的接口。
    org.springframework.context.ApplicationEventPublisher,一般一個Spring org.springframework.context.ApplicationContext,可以使用ApplicationEventMulticaster作為實際發布事件的委派。
  11. deduceMainApplicationClass
    根據執行點的StackTraceElement,棧幀來與"main"比較,判斷主應用類。
  12. StopWatch
    簡單的秒表,允許許多任務的時間點,暴露總共運行事件和每個命名任務的運行事件。隱藏Sys#nanoTime()的使用,提供應用代碼的可讀性,減少計算錯誤的可能性。注意,這個對象不是設計成線程安全的,并且不使用同步。這個類通常被用于驗證概念性證明工作中和開發中的性能,而不是作為生產應用的部分。在Spring Framework 5.2中,運行時被跟蹤,并且以毫秒報告。
  13. start()
    開啟一個未命名的任務。如果未先調用這個方法,那么stop()或者時機方法的結果是未定義的。
  14. ConfigurableApplicationContext
    由大多數應用上下文(如果不是所有)實現的SPI接口。
    提供功能配置一個應用上下文,加上在org.springframework.context.ApplicationContext接口中的應用上下文客戶端方法。配置和生命周期方法在這里封裝,未了避免讓它們對ApplicationContext客戶端代碼明顯。出現的方法應該只在啟動和關閉代碼中使用。
  15. SpringBootExceptionReporter
    函數式接口。用來支持SpringApplication啟動錯的自定義報告。SpringBootExceptionReporter通過SpringFactoriesLoader加載,并且必須聲明由一個單一ConfigurableApplicationContext參數的公共構造器。
  16. boolean reportException
    報告一個啟動失敗給用戶
  17. SpringApplicationRunListeners
    一組SpringApplicationRunListener。
  18. SpringApplicationRunListener
    SpringApplication#run方法的監聽器。SpringApplicationRunListener通過SpringFactoriesLoader加載,并且應該聲明一個公共構造器,接收一個SpringApplication實例,和一個String[] 參數。一個新的SpringApplicationRunListener實例會為每次運行創建。
  19. 類ApplicationStartingEvent
    盡早發布的事件,只要SpringApplication已經被啟動——在Environment或者ApplicationContext可用之前,但是在ApplicationListener已經被注冊后。事件源是SpringAplication自身,但是小心在早期階段使用它的內部狀態過多,因為它可能在后面的生命周期中被修改。
  20. 接口ConfigurableEnvironment
    被多數Environment類型實現的接口。提供功能:設置激活、默認profile、操作底層屬性源。允許客戶端通過ConfigurablePropertyResolver超接口設置、驗證要求的屬性、自定義邊界服務等等。
    操作屬性源。屬性元可以被移除、重排序或者替換;并且額外的屬性元可以使用MutablePropertySources實例添加,就是從getPropertySources()返回的。下面的例子依據ConfigurableEnvironment的StandardEnvironment實現,但是通常對任何實現可用,即時默認屬性元可能不同。
    例子:添加一個有更高搜索優先級的新屬性源:
ConfigurableEnvironment environment =  new StandardEnvironment();
MutablePropertySources propertySources = environment.getPropertySources();
Map<String, String> myMap = new HashMap<>();
myMap.put("xyz", "myValue");
propertySources.addFirst(new MapPropertySource("My_MAP",  myMap));

例子:移除默認系統屬性源

MutablePropertySources propertySources = environment.getPropertySources();
propertySources.remove(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME);

例子:為了測試目的,模擬系統環境

MutablePropertySources propertySources = environment.getPropertySources();
MockPropertySource mockEnvVars  = new MockPropertySource().withProperty("xyz", "myValue");
propertySources.replace(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME);

當一個Environment被ApplicationContext使用時,有一點是比較重要的,就是PropertySource操作要在上下文的org.springframework.context.support.AbstractApplicationContext#refresh()方法調用之前執行。這確保所有屬性源在容器引導過程中都可用,包括通過org.springframework.context.support.PropertySourcesPlaceholderConfigurer屬性占位符使用。

  1. prepareEnvironment
    1. 創建Env
    2. 配置Env,就是屬性、profile
    3. 向監聽器發送env準備好的事件
    4. 綁定env到SpringApplication
    5. 返回env對象
  2. configureIgnoreBeanInfo(COnfigurableEnvironment environment)
  3. createApplicationContext
    根據應用類型,反射實例化上下文類:SERVLET-org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;REACTIVE-org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;default-org.springframework.context.annotation.AnnotationConfigApplicationContext
  4. prepareContext()
    1. 給上下文設置env
    2. 后置處理應用上下文。應用任何緊密相關的后置處理ApplicationContext,如果需要,子類可以應用額外的處理:配置bean名稱生成器、設置資源加載器、添加便捷服務
    3. 應用任何ApplicationContextInitializer到上下文,在上下文被刷新前。
      1. 獲取初始化器。返回要被應用到Spring ApplicationContext的ApplicationContextInitializer集合
    4. 給監聽器發送上下文已準備好事件。就是ApplicationContext已經被創建、準備好,但是在源被加載前調用
    5. 獲取上下文內部的bean工廠,主備手動注冊各種bean
    6. 加載源的bean到應用上下文
    7. 給監聽器發送上下文準備好的事件。在應用上下文已經被加載,但是在應用上下文被刷新前調用。
  5. refreshContext()
    刷新應用上下文。最后調用的是一個同步方法。一次只能有一個線程調用。刷新配置的持久化表示,可能是XML文件、屬性文件或者關系型數據庫
  6. refresh()
    1. prepareRefresh():為刷新準備這個上下文,設置它的啟動日期和激活標志,包括執行屬性源的初始化
      1. 設置啟動日期
      2. 設置激活標志為true
      3. 初始化屬性源。初始化在上下文環境中的任意占位符屬性源。用實際實例替換存根屬性源
      4. 驗證所有被標記為要求的屬性為可解析的:看ConfigurablePropertyResolver#setRequiredProperties
      5. 存儲預刷新ApplicationListeners
      6. 允許所有早期ApplicationEvents集合,一旦廣播可用就發布
    2. obtainFreshbeanFactory():告訴子類刷新內部bean工廠。其實就是執行真實的配置加載,返回bean工廠。
    3. prepareBeanFactory():準備bean工廠,在這個上下文中使用該bean工廠。就是配置該工廠的標準上下文特征,例如上下文的類加載器和后置處理器。
    4. postProcessBeanFactory():允許在上下文子類中bean工廠的后置處理。在bean工廠的標準初始化后,修改該應用上下文的內部bean工廠。所有bean定義會被已經加載,但是還沒有bean已經被實例化。這允許在某種ApplicationContext實現中注冊特殊的BeanPostProcessors等等。
    5. invokeBeanFactoryPostProcessors():調用在該上下文中注冊為bean的工廠處理器。實例化并且調用所有注冊的BeanFactoryPostProcessor bean。
    6. registerBeanPostProcessors():注冊bean處理器,攔截bean創建
    7. initMessageSource():為這個上下文初始化消息源
    8. initApplicationEventMulticaster():為這個上下文初始化事件廣播。初始化ApplicationEventMulticaster。如果在該上下文中沒有定義,使用SimpleApplicationEventMulticaster。現在才注冊???
    9. onRefresh():初始化其他在特定上下文子類中的具體bean
    10. registerListeners():檢查監聽器bean,并且注冊它們
    11. finishBeanFactoryInitialization():初始化所有保留的(非延遲初始化)單例。完成這個上下文的bean工廠的初始化,實例化所有剩余單例bean。
    12. finishRefresh():發布相應的事件
    13. destroyBeans():銷毀已經創建的單例,避免懸掛資源
    14. cancelRefresh():重置active標志
    15. resetCommonCaches():重置Spring內核中公共的內省緩存,因為我們可能不再需要單例bean的元數據。
  7. AbstractAplicationContext類還是很重要的
  8. afterRefresh()
    在上下文已經被刷新后調用
  9. stop()
    停止秒表
  10. stated()
    給監聽器發送事件,就是上下文已經被刷新,并且應用已經啟動,但是CommandLineRunner和ApplicationRunner還沒有被調用
    36.接口 ApplicationRunner
    一個函數式ean包含在SpringApplication中時,它是否應該運行。多個ApplicationRunner bean可以被定義在相同的應用上下文中,并且可以用Ordered接口或者@Order排序。
  11. running()
    在run方法完成前,立刻調用,當應用上下文已經被刷新,并且所有的CommandLineRunner和ApplicationRunner已經被調用后。
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370