spring boot 2啓動過程整理

Spring Boot 2 特性


摘自官網java

  • Create stand-alone Spring applications
  • Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
  • Provide opinionated 'starter' dependencies to simplify your build configuration
  • Automatically configure Spring and 3rd party libraries whenever possible
  • Provide production-ready features such as metrics, health checks and externalized configuration
  • Absolutely no code generation and no requirement for XML configuration

前言


目前 spring boot 已被普遍使用,關於 spring boot 的文章更是隨處可見。所以,咱們就串聯瑣碎的知識點,簡單整理出一條線。使用spring boot 已經有一段時間了,可是有些知識點仍是比較模糊。它啓動過程都幹了什麼?如何實現自動配置的?啓動 web 應用的時候何時啓動 tomcat 服務的?發起一個 http 請求會經歷什麼過程?react

啓動過程


spring boot 啓動過程的源碼分析網上有不少,並且比較詳細,就再也不詳細贅述了。有興趣的同窗自行翻閱資料哈~web

在 spring boot 啓動的時候,會根據工程的環境(主要是根據特定的類名稱)肯定一個 ConfigurableApplicationContext 的上下文對象,咱們就以 AnnotationConfigServletWebServerApplicationContext 作例子。spring

// 建立ConfigurableApplicationContext,若是是servlet環境會建立AnnotationConfigServletWebServerApplicationContext
context = createApplicationContext();
// 從spring.factories中獲取SpringBootExceptionReporter工廠,並初始化spring boot的異常分析對象
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
    new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新appContext,這裏開始啓動spring管理bean
refreshContext(context);
// afterRefresh方法是protected的空實現,咱們能夠用這個鉤子作一些事情
afterRefresh(context, applicationArguments);

重點關注 refreshContext 方法,這個方法強制轉換對象爲 AbstractApplicationContext 後執行他的 refresh 方法瀏覽器

protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    ((AbstractApplicationContext) applicationContext).refresh();
}

常常看 spring 源碼的同窗,看到這一步應該就比較熟悉了,和咱們傳統的 SSM 框架同樣的啓動流程。tomcat

啓動過程當中功能簡述springboot

  • 經過 SpringFactoriesLoader 提供通用的工廠加載機制,具體是解析工程下全部jar包中的 META-INF/spring.factories 文件,並提供反序列化 bean 功能
  • 提供 PropertiesYaml 配置文件的解析,以及數據綁定
  • 推斷上下文環境是 SERVLETREACTIVENON, 以建立相應的 ConfigurableApplicationContext 來管理 Bean 的生命週期
  • 支持可擴展 SPI 接口,包括 PropertySourceLoaderSpringApplicationRunListenerSpringBootExceptionReporterApplicationContextInitializerApplicationListenerEnvironmentPostProcessorFailureAnalyzerFailureAnalysisReporter

瞭解了 SpringFactoriesLoader 實現相似 java spi 的功能,咱們就能夠自定義實現 SPI 的接口。實現方式:app

  1. 以 java config 形式配置託管,例如:@Component
  2. 在執行 run 返回的 ConfigurableApplicationContext 中添加(部分接口能夠)
  3. 工程中新建 META-INF/spring.factories 文件,以 接口=實現 的形式存放

實現自動配置


咱們的 spring boot 的啓動類一般會使用 @SpringBootApplication ,該註解包含了@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan,而 @EnableAutoConfiguration 是實現自動配置的關鍵框架

...
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    ...
}

在前面咱們提到 spring boot 啓動會執行到AbstractApplicationContext.refresh(),以後調用ConfigurationClassPostProcessor.processConfigBeanDefinitions掃描全部的 @Import ,並執行 AutoConfigurationImportSelectorselectImports,該方法調用 SpringFactoriesLoader 讀取spring.factories獲取 EnableAutoConfiguration 對應的實現類。ide

方法執行棧
refresh --> invokeBeanFactoryPostProcessors --> ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry --> AutoConfigurationImportSelector.selectImports

最後會在 AbstractApplicationContext.finishBeanFactoryInitialization 初始化非懶加載的對象,完成自動化配置。

Web 服務的啓動


首先咱們來看下 AnnotationConfigServletWebServerApplicationContext 類關係圖,因爲圖比較大就不放出來了。
AnnotationConfigServletWebServerApplicationContext --> ServletWebServerApplicationContext --> GenericWebApplicationContext --> GenericApplicationContext --> AbstractApplicationContext
因此在執行 AbstractApplicationContext.refresh 中調用 onRefresh 時,會調用 ServletWebServerApplicationContext.onRefresh
再調用 createWebServer

// ServletWebServerApplicationContext.createWebServer
private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    //若是配有配置WebServer,則從BeanFactory中獲取一個ServletWebServerFactory的實例
    if (webServer == null && servletContext == null) {
        ServletWebServerFactory factory = getWebServerFactory();
        this.webServer = factory.getWebServer(getSelfInitializer());
    }
    else if (servletContext != null) {
        try {
            // 調用selfInitialize,配置servletContext,註冊servletContext到spring context中,並調用ServletContextInitializer的onStartup鉤子方法
            getSelfInitializer().onStartup(servletContext);
        }
        catch (ServletException ex) {
            throw new ApplicationContextException("Cannot initialize servlet context", ex);
        }
    }
    // 初始化servletContext配置
    initPropertySources();
}

總結


最後,咱們經過一圖簡單解答前言中提到的問題。

springboot.png

當 spring boot 啓動完成後,正常狀況下一次 http 請求大體會經歷這樣一個過程。用戶輸入訪問地址 --> 瀏覽器客戶端發起請求 --> web 服務(tomcat、jetty)轉發請求 --> 請求處理(servlet規範、reactive規範、其餘自定義的) --> 響應結果

注:第一次發博客,不免有所不足,甚至出現錯誤的地方,歡迎你們指正,謝謝。

相關文章
相關標籤/搜索