Springboot淺析(二)——容器啓動流程

大概是水平有限,最近跟讀代碼與相關書籍感受巨費時間,想深刻弄明白全部的東西很難,因此也只能帶着問題來學習springboot了,之後遇到確切的問題再作深刻了解把,給本身定個目標,暫時只弄清楚容器啓動大致流程,瞭解組件掃描,自動配置,解決循環依賴這幾個問題。 通常啓動的Main方法爲SpringApplication.run(啓動類.class, args);,跟下去的話會發現調用的就是new SpringApplication(啓動類).run(args)因爲容器刷新內容最關鍵也最複雜,先來了解下除容器刷新以外的流程。java

(一) SpringApplication的初始化

1.代碼

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { 
  this.resourceLoader = resourceLoader;
  Assert.notNull(primarySources, "PrimarySources must not be null"); 
  //一般狀況下primarySources就是啓動類,暫時理解這裏就是將啓動類設置爲主配置資源來源
 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); 
 //經過類路徑中尋找相關類,判斷當前環境是NONE(標準環境(classPath下沒有javax.servlet.Servlet以及org.springframework.web.context.ConfigurableWebApplicationContext)、SERVLET(Servlet環境)、REACTIVE(響應式)
 this.webApplicationType = WebApplicationType.deduceFromClasspath();
//添加initializers,設置初始化器,這些初始化器將在在容器刷新前回調,原理是經過SpringFactoriesLoader的loadFactoryNames方法在 spring.factories文件中找到的ApplicationContextInitializer接口的配置的實現類的全限定類名,並實例化。
  setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
 //同上,設置ApplicationListener,添加 spring.factories文件中ApplicationListener配置的響應實現類。
  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); 
  //經過構造一個運行時異常,而後去棧幀中尋找方法名爲main的方法來獲得入口類的名字並設置爲mainApplicationClass
 this.mainApplicationClass = deduceMainApplicationClass(); 
}

2.注

(1)ApplicationContextInitializer有哪些?

debug發現有這些:web

他們的做用是:spring

  • ConfigurationWarningsApplicationContextInitializer:報告IOC容器的一些常見的錯誤配置
  • ContextIdApplicationContextInitializer:設置Spring應用上下文的ID
  • DelegatingApplicationContextInitializer:加載 application.properties 中 context.initializer.classes 配置的類
  • ServerPortInfoApplicationContextInitializer:將內置servlet容器實際使用的監聽端口寫入到 Environment 環境屬性中
  • SharedMetadataReaderFactoryContextInitializer:建立一個 SpringBoot 和 ConfigurationClassPostProcessor 共用的 CachingMetadataReaderFactory 對象
  • ConditionEvaluationReportLoggingListener:將 ConditionEvaluationReport 寫入日誌

(2)ApplicationListener有哪些

debug發現有這些:編程

他們的做用是:緩存

  • ClearCachesApplicationListener:應用上下文加載完成後對緩存作清除工做
  • ParentContextCloserApplicationListener:監聽雙親應用上下文的關閉事件並往本身的子應用上下文中傳播
  • FileEncodingApplicationListener:檢測系統文件編碼與應用環境編碼是否一致,若是系統文件編碼和應用環境的編碼不一樣則終止應用啓動
  • AnsiOutputApplicationListener:根據 spring.output.ansi.enabled 參數配置 AnsiOutput
  • ConfigFileApplicationListener:從常見的那些約定的位置讀取配置文件
  • DelegatingApplicationListener:監聽到事件後轉發給 application.properties 中配置的 context.listener.classes 的監聽器
  • ClasspathLoggingApplicationListener:對環境就緒事件 ApplicationEnvironmentPreparedEvent 和應用失敗事件 ApplicationFailedEvent 作出響應
  • LoggingApplicationListener:配置 LoggingSystem。使用 logging.config 環境變量指定的配置或者缺省配置
  • LiquibaseServiceLocatorApplicationListener:使用一個能夠和 SpringBoot 可執行jar包配合工做的版本替換 LiquibaseServiceLocator
  • BackgroundPreinitializer:使用一個後臺線程儘早觸發一些耗時的初始化任務

(3)REACTIVE是什麼

REACTIVE是響應式編程的東西,指的是應用WebFlux框架下的應用環境,是NIO同步非阻塞IO,將來可能替代當前的MVC,因爲是比較新的技術,應用場景比較有限,暫時不作深刻了解。springboot

(二)容器刷新以前的操做

1.代碼

public ConfigurableApplicationContext run(String... args) {  
//這個組件是用來監控啓動時間的,不是很重要
StopWatch stopWatch = new StopWatch();  
stopWatch.start();  
ConfigurableApplicationContext context = null;  
//SpringBootExceptionReporter這個東西是一個異常解析器,實現類只有一個是FailureAnalyzers,
//用於打印異常信息,這個集合在下面③處會初始化,集合裏面裝了針對各式各樣的解析器,
//在catch到異常後,會遍歷這個集合,尋找合適的解析器,而後打印異常日誌
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); 
//刷新系統屬性java.awt.headless的值,若是沒有值則設爲true,這個值表示無頭模式(意指缺乏顯示設備,鍵盤或鼠標的系統配置),
//在無頭模式下java.awt.Toolkit將使用特定的無頭模式下的實現類,由於就算沒有顯示設備,有些操做任可以被容許。
configureHeadlessProperty();  
//①
SpringApplicationRunListeners listeners = getRunListeners(args);  
listeners.starting();  
try {  
//②
  ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);  
  ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
  //配置系統參數spring.beaninfo.ignore,默認值爲ture,字面意思是跳過搜索BeanInfo類,但具體是什麼我暫時也不清楚。
  configureIgnoreBeanInfo(environment);
  //③
  Banner printedBanner = printBanner(environment);  
  //④
  context = createApplicationContext();  
  //⑤
  exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,  
 new Class[]{ConfigurableApplicationContext.class}, context);  
 //⑥
  prepareContext(context, environment, listeners, applicationArguments, printedBanner);  
  refreshContext(context);  
  //刷新後的處理,是個空實現
  afterRefresh(context, applicationArguments); 
  //計時器結束
  stopWatch.stop();  
 if (this.logStartupInfo) {  
  new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);  
  }  
  //發佈started事件
  listeners.started(context);  
  //運行器回調,即實現了ApplicationRunner接口或CommandLineRunner接口的bean
  callRunners(context, applicationArguments);  
} catch (Throwable ex) {  
  handleRunFailure(context, ex, exceptionReporters, listeners);  
 throw new IllegalStateException(ex);  
}  
  
try {  
  listeners.running(context);  
} catch (Throwable ex) {  
  handleRunFailure(context, ex, exceptionReporters, null);  
 throw new IllegalStateException(ex);  
}  
return context;
}

2.代碼註釋

內部又是調getSpringFactoriesInstances方法,取spring.factories中全部的SpringApplicationRunListener,而後對外暴露SpringApplicationRunListeners。 SpringApplicationRunListeners封裝全部的SpringApplicationRunListener,用於容器啓動間的事件發佈到全部的SpringApplicationRunListener中。app

SpringApplicationRunListener中定義的方法有:框架

  • void starting();首次啓動run方法時當即調用。可用於很是早期的初始化。
  • void environmentPrepared(ConfigurableEnvironment environment);準備好環境(Environment構建完成),但在建立ApplicationContext以前調用。
  • void contextPrepared(ConfigurableApplicationContext context);在建立和構建ApplicationContext以後,但在加載以前調用。
  • void contextLoaded(ConfigurableApplicationContext context);ApplicationContext已加載但在刷新以前調用。
  • void started(ConfigurableApplicationContext context);ApplicationContext已刷新,應用程序已啓動,但還沒有調用CommandLineRunners和ApplicationRunners
  • void running(ConfigurableApplicationContext context);在運行方法完全完成以前當即調用,刷新ApplicationContext並調用全部CommandLineRunners和ApplicationRunner。
  • void failed(ConfigurableApplicationContext context, Throwable exception);在運行應用程序時失敗時調用。

值得注意的是,started、running、failed方法是 SpringBoot2.0 才加入的。less

經過Debug,發現默認狀況下加載的listeners有一個,類型爲 EventPublishingRunListener。它在SpringBoot應用啓動的不一樣時間點發布不一樣應用事件類型(ApplicationEvent),若是有哪些事件監聽者(ApplicationListener)對這些事件感興趣,則能夠接收而且處理。SpringApplicationRunListener與ApplicationListener的區別是SpringApplicationRunListener比ApplicationListener更靠前,SpringApplicationRunListener監聽的是SpringApplication相關方法的執行,屬於第一層監聽器,他會發布相應的事件給ApplicationListener。學習

根據不一樣的webApplicationType完成Environment的初始化,通常是使用StandardServletEnvironment實現類,Environment用於描述應用程序當前的運行環境,其抽象了兩個方面的內容:配置文件(profile)和屬性(properties),其實就是對應的配置文件、環境變量、命令行參數裏面的內容。這裏Environment構建完成時發佈了environmentPrepared事件,而且將最新的配置值綁定到了SpringbootApplication中,也就是當前的對象中。好比yml裏面配的spring. main開頭的一些屬性值。

根據Enviroment中配置獲取對應的banners沒有則用默認的SpringbootBanner打印啓動信息,就是啓動應用時候控制檯打印的logo

根據WebApplicationType,反射建立不一樣的ApplicationContext實現(Servlet是AnnotationConfigServletWebServerApplicationContext)。這裏Servlet是AnnotationConfigServletWebServerApplicationContext,在他的父類GenericApplicationContext構造方法中,其中注入了一個DefaultListableBeanFactory,這個BeanFactory很關鍵,實際上AnnotationConfigServletWebServerApplicationContext的BeanFactory能力就是從DefaultListableBeanFactory擴展而來。 另外在這一步中也註冊了ConfigurationClassPostProcessor、DefaultEventListenerFactory、EventListenerMethodProcessor、AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor這些beanDefinition,做爲基礎組件。ConfigurationClassPostProcessor這個組件是最重要的,其餘的暫時沒有深究什麼做用,ConfigurationClassPostProcessor是BeanFactoryPostProcessor,負責在容器刷新時加載掃描配置類註解進行組件解析,註冊BeanDefinition。

建立一系列SpringBootExceptionReporter,建立流程是經過SpringFactoriesLoader獲取到全部實現SpringBootExceptionReporter接口的class,

初始化ApplicationContext,主要完成如下工做:

  • 將準備好的Environment設置給ApplicationContext
  • 進一步執行ApplicationContext的後置處理,包括註冊BeanName生成器, 設置資源加載器和類加載器,設置類型轉換器ConversionService等等,這裏的東西暫時不用深究。
  • 遍歷調用全部的ApplicationContextInitializer的 initialize()方法來對已經建立好的ApplicationContext進行進一步的處理。
  • 調用SpringApplicationRunListener的 contextPrepared()方法,通知全部的監聽者:ApplicationContext已經準備完畢。
  • 建立啓動類的beanDefiniton註冊到容器中。
  • 調用SpringApplicationRunListener的 contextLoaded()方法,通知全部的監聽者:ApplicationContext已經裝載完畢。

小結

容器刷新前,整個流程分三個步驟:

  1. 初始化SpringApplication對象,好比設置webApplicationType,加載ApplicationListener,ApplicationContextInitializer。
  2. 初始化Environment對象,封裝配置文件,命令行參數。
  3. 初始化ConfigurableApplicationContext(與ApplicationContext的區別是ConfigurableApplicationContext能夠對容器進行寫,而ApplicationContext只提供讀的方法),而且將啓動類的beanDefiniton先行註冊到容器中。 這是主線程能夠看到的,其餘沒有看到的ApplicationContextInitializer與ApplicationListener幹了什麼暫時尚未進行深究。
相關文章
相關標籤/搜索