Spring启动流程[4]刷新上下文【1】

1,065 阅读4分钟

4.刷新上下文

这里对应的代码在run方法中如下:

 refreshContext(context);
 ​
     private void refreshContext(ConfigurableApplicationContext context) {
         if (this.registerShutdownHook) {
             shutdownHook.registerApplicationContext(context);
         }
         refresh(context);
     }
     
     protected void refresh(ConfigurableApplicationContext applicationContext) {
         applicationContext.refresh();
     }
题外话 - 优雅下线相关

这里的shutdownHook,是用来让Springboot服务优雅下线的。

什么叫优雅下线?一般来说,下线会用例如像直接kill进程等命令,显得很粗暴也不好管理;SpringBoot的优雅下线一般是通过调用接口,来关闭服务,这样子可以很简单地接到各个平台上,也没有那么粗暴了。

这个registerShutdownHook默认值为true,如果是通过例子那样通过context启动,而非自定义context的话,那么这个值在过程中没有被修改。


接下来来看看本期主角:这个refresh方法。

 //ConfigurableApplicationContext
 void refresh() throws BeansException, IllegalStateException;

这里的refresh被三个类实现了,其中两个和我们的这个AnnotationConfigServletWebServerApplicationContext有关:

  • AbstractApplicationContext
  • ServletWebServerApplicationContext

根据继承树,这里来看看最近的ServletWebServerApplicationContext

 @Override
 public final void refresh() throws BeansException, IllegalStateException {
    try {
       super.refresh();
    }
    catch (RuntimeException ex) {
       WebServer webServer = this.webServer;
       if (webServer != null) {
          webServer.stop();
       }
       throw ex;
    }
 }

实际上大体上依然是调用父类AbstractApplicationContext的refresh方法,只是在refresh方法抛出异常时,做了额外处理:关闭当前的webServer。

所以本期的重点就集中在这个AbstractApplicationContext的refresh方法上了,来看看代码:

 public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
       StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
 ​
       // Prepare this context for refreshing.
       prepareRefresh();
 ​
       // Tell the subclass to refresh the internal bean factory.
       ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 ​
       // Prepare the bean factory for use in this context.
       prepareBeanFactory(beanFactory);
 ​
 //......................
    }
 }

这里就很复杂了,此处先关注这几行,剩下的留到下一篇:

 synchronized (this.startupShutdownMonitor) {
    StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
 ​
    // Prepare this context for refreshing.
    prepareRefresh();
 ​
    // Tell the subclass to refresh the internal bean factory.
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 ​
    // Prepare the bean factory for use in this context.
    prepareBeanFactory(beanFactory);

这里新建了一个StartupStep,根据上下文以及类的注解,这个类单纯是用来标注一下context并记录一下日志的辅助工具,不再关注。

接下来看看这里涉及的三个方法。

4.1 准备刷新

对应代码如下:

 // Prepare this context for refreshing.
 prepareRefresh();
 //
     protected void prepareRefresh() {
         // Switch to active.
         this.startupDate = System.currentTimeMillis();
         this.closed.set(false);
         this.active.set(true);
 ​
         if (logger.isDebugEnabled()) {
             if (logger.isTraceEnabled()) {
                 logger.trace("Refreshing " + this);
             }
             else {
                 logger.debug("Refreshing " + getDisplayName());
             }
         }
 ​
         // Initialize any placeholder property sources in the context environment.
         initPropertySources();
 ​
         // Validate that all properties marked as required are resolvable:
         // see ConfigurablePropertyResolver#setRequiredProperties
         getEnvironment().validateRequiredProperties();
 ​
         // Store pre-refresh ApplicationListeners...
         if (this.earlyApplicationListeners == null) {
             this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
         }
         else {
             // Reset local application listeners to pre-refresh state.
             this.applicationListeners.clear();
             this.applicationListeners.addAll(this.earlyApplicationListeners);
         }
 ​
         // Allow for the collection of early ApplicationEvents,
         // to be published once the multicaster is available...
         this.earlyApplicationEvents = new LinkedHashSet<>();
     }

上面就是做了一下简单的初始化工作:

 this.startupDate = System.currentTimeMillis();
 this.closed.set(false);
 this.active.set(true);
 ​
 if (logger.isDebugEnabled()) {
    if (logger.isTraceEnabled()) {
       logger.trace("Refreshing " + this);
    }
    else {
       logger.debug("Refreshing " + getDisplayName());
    }
 }

设置变量,记录日志,没了。

4.1.1 初始化配置源

 // Initialize any placeholder property sources in the context environment.
 initPropertySources();
 ​
     /**
      * <p>Replace any stub property sources with actual instances.
      * @see org.springframework.core.env.PropertySource.StubPropertySource
      * @see org.springframework.web.context.support.WebApplicationContextUtils#initServletPropertySources
      */
     protected void initPropertySources() {
         // For subclasses: do nothing by default.
     }

这里是一个模板方法,根据提示我们找到了GenericWebApplicationContext里的对应方法:

     /**
      * {@inheritDoc}
      * <p>Replace {@code Servlet}-related property sources.
      */
 protected void initPropertySources() {
    ConfigurableEnvironment env = getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
       ((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, null);
    }
 }

根据注解,这里就是将环境对象env中和servlet相关的变量替换掉。而之前在流程中并没有配置过这个servletContext,因此这里是个null,接下去其实也不执行了,因此此处不再细究。

4.1.2 验证环境配置

 getEnvironment().validateRequiredProperties();

实际上这一步依然不执行,因此一样也不在细究。

4.1.3 监听者相关

 // Store pre-refresh ApplicationListeners...
 if (this.earlyApplicationListeners == null) {
    this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
 }
 else {
    // Reset local application listeners to pre-refresh state.
    this.applicationListeners.clear();
    this.applicationListeners.addAll(this.earlyApplicationListeners);
 }

根据之前的文章容易知道,applicationListener在Springboot也就是本系列文章的内容内是一个很关键的扩展点,大部分扩展事件都是通过这个来执行的。因此这里就是两个容器来回倒腾,总之这里就是让

earlyApplicationListenersapplicationListeners这俩兄弟务必一致咯。

最后就是初始化了earlyApplicationEvents,根据注解,一旦轮播者可用了事件就发出去了:

 // Allow for the collection of early ApplicationEvents,
 // to be published once the multicaster is available...
 this.earlyApplicationEvents = new LinkedHashSet<>();

4.2 获取并刷新beanFactory

这里对应代码如下:

 // Tell the subclass to refresh the internal bean factory.
 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 ​
     protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
         refreshBeanFactory();
         return getBeanFactory();
     }

这里依然用的是模板方法,对应到的类是GenericApplicationContext。相信到这里还记得这个之前提过的类,实际上使用的context中的DefaultListableBeanFactory就是在这个父类构造方法中创建的。

不过这里的refresh显得很水,更像是打标记:

 protected final void refreshBeanFactory() throws IllegalStateException {
    if (!this.refreshed.compareAndSet(false, true)) {
       throw new IllegalStateException(
             "GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
    }
    this.beanFactory.setSerializationId(getId());
 }

至于get,这里的重写方法就是普通的set、get封装了,不再赘述。

4.3 准备beanFactory

上一步很敷衍地刷新并获取了beanFactory,这里就用到了:

 // Prepare the bean factory for use in this context.
 prepareBeanFactory(beanFactory);
 ​
     protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
         // Tell the internal bean factory to use the context's class loader etc.
         beanFactory.setBeanClassLoader(getClassLoader());
         if (!shouldIgnoreSpel) {
             beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
         }
         beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
 ​
         // Configure the bean factory with context callbacks.
         beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
         beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
         beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
         beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
         beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
         beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
         beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
         beanFactory.ignoreDependencyInterface(ApplicationStartupAware.class);
 ​
         // BeanFactory interface not registered as resolvable type in a plain factory.
         // MessageSource registered (and found for autowiring) as a bean.
         beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
         beanFactory.registerResolvableDependency(ResourceLoader.class, this);
         beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
         beanFactory.registerResolvableDependency(ApplicationContext.class, this);
 ​
         // Register early post-processor for detecting inner beans as ApplicationListeners.
         beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
 ​
         // Detect a LoadTimeWeaver and prepare for weaving, if found.
         if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
             beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
             // Set a temporary ClassLoader for type matching.
             beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
         }
 ​
         // Register default environment beans.
         if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
             beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
         }
         if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
             beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
         }
         if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
             beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
         }
         if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) {
             beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup());
         }
     }

首先先给BeanFactory设置一个类加载器。由于之前没有设置这个变量,因此这里取得的是默认的类加载器。

如果没有设置忽略Spel表达式的选项,那么这里会给BeanFactory配置一个表达式解析器。

随后根据实际的需要,配置、忽略了一些相关的变量。这么多东西不是必须记忆的东西,就放到BeanFactory中进行细究吧。

总结

本篇文章主要解析了启动方法中刷新上下文中的:

  • 准备其他工具,以及beanFactory为启动而做的特殊处理的步骤。

算是在刷新上下文这个启动过程最重要的步骤中的前菜了,这些工作属于在正式工作开始之前的统一准备处理。