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的原理。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值