spring boot原理分析(九):上下文Context即世界2

spring boot原理分析(九):上下文Context即世界2

前言

    上下文Context可以說spring boot中最重要的一個概念,不僅包含了tomcat和spring mvc的啓動和管理,還對spring mvc原有模式中的bean註冊進行了大幅簡化,理解Spring boot的Context可以說是理解spring boot的基礎。
    原理分析(八)已經介紹了Spring boot的ApplicationContext的定義實現和創建,其中就包含了上下文Context攜帶了哪些必要的信息,能夠起到哪些作用。本文主要對上下文的準備和刷新進行介紹。
    首先還是對齊spring boot啓動總流程中run方法的相對應的信息。在上下文Context構建完成之後,接下來就是對上下文的準備和刷新,如下代碼所示。

......
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
......

上下文準備

    prepareContext方法中實現了上下文相關的初始化。具體內容如下注釋。

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
    SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
  //1.設置環境
  context.setEnvironment(environment);
  //2.後處理上下文設置
  postProcessApplicationContext(context);
  //3.初始化上下文
  applyInitializers(context);
  //4.事件監聽器發送上下文準備完畢事件
  listeners.contextPrepared(context);
  if (this.logStartupInfo) {
    logStartupInfo(context.getParent() == null);
    logStartupProfileInfo(context);
  }
  // Add boot specific singleton beans
  ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
  //5.bean工廠中註冊應用程序參數bean
  beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
  if (printedBanner != null) {
    //6.bean工廠中註冊條幅bean
    beanFactory.registerSingleton("springBootBanner", printedBanner);
  }
  if (beanFactory instanceof DefaultListableBeanFactory) {
    ((DefaultListableBeanFactory) beanFactory)
        .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
  }
  // 7.注入基礎類
  Set<Object> sources = getAllSources();
  Assert.notEmpty(sources, "Sources must not be empty");
  load(context, sources.toArray(new Object[0]));
  // 8.事件監聽器發送上下文加載完成事件
  listeners.contextLoaded(context);
}
    1. 環境設置:上下文中包含了系統的環境相關的信息,主要是profile和properties配置,properties配置不僅包括項目內的properties文件,還有JVM system properties、操作系統環境變量等。
    1. 後處理上下文設置:
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
  //如果有,設置bean name生成器
  if (this.beanNameGenerator != null) {
    context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
        this.beanNameGenerator);
  }
  //如果有,設置資源加載器
  if (this.resourceLoader != null) {
    if (context instanceof GenericApplicationContext) {
      ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
    }
    if (context instanceof DefaultResourceLoader) {
      ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
    }
  }
  //設置Converter管理器
  if (this.addConversionService) {
    context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
  }
}

    默認情況下,這裏是沒有設置bean name生成器和資源加載器,如果需要,可以自己設置。不過,最後的ConverterService是會被設置的。Converter組件是用來做參數轉換的,比如String到日期的轉換等,這些轉換器都由ConversionService管理。

    1. 初始化上下文用到了ApplicationContextInitializer的子類實例:
protected void applyInitializers(ConfigurableApplicationContext context) {
  for (ApplicationContextInitializer initializer : getInitializers()) {
    Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
        ApplicationContextInitializer.class);
    Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
    //實現上線文的初始化
    initializer.initialize(context);
  }
}

如果你還記得,上下文初始化子類的設置是在SpringApplication類的構造函數中完成的。

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

上下文初始化子類,可以在上下文中設置一些必要的屬性。

    1. 4和8一樣,這部分已經在spring boot的事件監聽中講過了。參考原理分析(七)
    1. bean工廠中註冊應用程序參數bean:將應用main中傳入的參數設置爲bean
    1. bean工廠中註冊條幅bean:spring boot條幅相關的在之前也講過,就是開始打印的很大的spring boot。
    1. 注入基礎類:這裏獲取了基礎類,其中就包括入口類,然後分析了註解並注入到bean中。

上下文刷新

    上下文刷新主要流程的邏輯不是在SpringApplication類中實現的。refreshContext中調用了refresh方法,而refresh方法回調了上下文Context的refresh方法。典型的我刷新我自己。

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

由於這裏的實現是ServletWebServerApplicationContext,所以調用的是這個類的refresh方法,再往下,實際上

public final void refresh() throws BeansException, IllegalStateException {
  try {
    super.refresh();
  }
  catch (RuntimeException ex) {
    stopAndReleaseWebServer();
    throw ex;
  }
}

調用的是父類AbstractApplicationContext的refresh,正是在這個refresh方法中,定義了上下文刷新的主要流程步驟。

@Override
public void refresh() throws BeansException, IllegalStateException {
  synchronized (this.startupShutdownMonitor) {
    // 1.爲上下文刷新作準備
    prepareRefresh();

    // 2.通知子類去刷新內部的bean工廠
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

    // 3.準備bean工廠
    prepareBeanFactory(beanFactory);

    try {
      //4. 允許bean工廠的後處理操作
      postProcessBeanFactory(beanFactory);

      //5. 觸發bean工廠中註冊的BeanFactoryPostProcessor
      invokeBeanFactoryPostProcessors(beanFactory);

      //6. 註冊BeanPostProcessor處理器:.
      registerBeanPostProcessors(beanFactory);

      //7. 初始化MessageSource
      initMessageSource();

      //8. 初始化事件監聽管理器
      initApplicationEventMulticaster();

      //9. 初始化Web服務器等(tomcat和spring mvc)
      onRefresh();

      //10. 檢查事件監聽處理器並註冊
      registerListeners();

      //11. 實例化所有最後存在的單例bean
      finishBeanFactoryInitialization(beanFactory);

      //12. 啓動Web服務器(tomcat和spring mvc)
      finishRefresh();
    }

    catch (BeansException ex) {
      if (logger.isWarnEnabled()) {
        logger.warn("Exception encountered during context initialization - " +
            "cancelling refresh attempt: " + ex);
      }
      destroyBeans();
      cancelRefresh(ex);
      throw ex;
    }

    finally {
      // Reset common introspection caches in Spring's core, since we
      // might not ever need metadata for singleton beans anymore...
      resetCommonCaches();
    }
  }
}
    1. 爲上下文刷新做準備:
          主要是做了環境屬性的配置,比如properties屬性讀入後存在一些佔位但未確定的屬性,這時就會被從context中讀出的配置所取代,完成環境配置的最終更新。還會做一些事件監聽器的初始化,這裏的事件監聽器是針對參考原理分析(七)事件的細化處理,比如LoggingApplicationListener就會監聽多個已經講過的事件。
    1. 通知子類去刷新內部的bean工廠:對beanFactory進行刷新並獲取
    1. 準備bean工廠:
          獲取Context的相關設置,對beanFactory進行配置。
      內部設置了bean的加載器、bean加載後處理器、事件發佈器、環境變量、應用配置等等。
    1. 允許bean工廠的後處理操作:
          這裏主要設置了使用註解加載的bean的後處理器以及普通bean的後處理器。關於bean的後處理器就是之前提到的BeanPostProcessor。
    1. 觸發bean工廠中註冊的BeanFactoryPostProcessor:
          BeanFactoryPostProcessor需要和4中的BeanPostProcessor區分開。BeanFactoryPostProcessor能夠在bean創建前修改bean的定義屬性。而BeanPostProcessor是在spring容器加載了bean的定義文件並且實例化bean之後執行的。
    1. 註冊BeanPostProcessor處理器:
          這裏代碼上的註釋和實際的內容有些出入,代碼上的註釋是註冊阻止bean創建的處理器,但是源碼裏的實際內容是註冊了各種處理器。可能是我理解有誤,有待確定。
    1. 初始化MessageSource:
          MessageSource是用來處理國際化的問題,這個之前有提到。
    1. 初始化事件監聽管理器:
          這裏的事件監聽管理器和後面的10的事件監聽處理器,在原理分析(七)已經有涉及到。
    1. 初始化Web服務器等(tomcat和spring mvc)
          這個方法是由子類ServletWebServerApplicationContext實現的
@Override
protected void onRefresh() {
  super.onRefresh();
  try {
    createWebServer();
  }
  catch (Throwable ex) {
    throw new ApplicationContextException("Unable to start web server", ex);
  }
}

這裏createWebServer方法是TomcatServletWebServerFactory.getWebServer方法,
初始化tomcat的容器。

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
  Tomcat tomcat = new Tomcat();
  File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
  tomcat.setBaseDir(baseDir.getAbsolutePath());
  Connector connector = new Connector(this.protocol);
  tomcat.getService().addConnector(connector);
  customizeConnector(connector);
  tomcat.setConnector(connector);
  tomcat.getHost().setAutoDeploy(false);
  configureEngine(tomcat.getEngine());
  for (Connector additionalConnector : this.additionalTomcatConnectors) {
    tomcat.getService().addConnector(additionalConnector);
  }
  prepareContext(tomcat.getHost(), initializers);
  return getTomcatWebServer(tomcat);
}

這裏就涉及到了Tomcat和spring mvc如何初始化的內容了。在我的tomcat + spring mvc原理系列文章中有比較詳細的介紹。

    1. 實例化所有最後存在的單例bean
    1. 啓動Web服務器(tomcat和spring mvc)
          調用了tomcat的start方法,整個服務被啓動了起來。

附:

    spring boot的上下文管理是依託於spring的bean的註冊和管理的基礎上的,所以其中涉及了較多bean工廠和處理器相關的內容。如果想要有更加清楚的認識,需要深入瞭解spring的原理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章