SpringBoot2启动流程深入分析


title: SpringBoot2.StartProcess.depth

StartProcess提纲

  • 启动监听器
  • 获取环境
  • 创建容器
  • 准备容器
  • 刷新容器
  • 刷新容器后扩展

昨天了咱们把大的过程说了一下,今天咱们把细化的过程详细写写。
之后再根据这几个细化过程,继续来一遍深入的源码解读。
一点关于源码阅读的心得就是先看大架构布局,然后细看各个组成模块。
明白各个模块用到的技术
通过上面的方式去看源码其实是很轻松的一件事情。
可能有人会说了,他妈的就是不知道才去看源码的呀
知道还看源码干啥
其实哦这样说也能理解,不过难道不可以看看官方文档明白架构?
不能看看别人的博客吸取的经验,然后再去看源码?
有些事情能站在别人的肩膀上就在别人肩膀上,时间就是生命就是金钱。
看源码的动力,你可以认为每弄懂一个框架。
年薪就能加10W
等超牛逼了年薪百万小问题,顺带着还有超强的成就感。
最屌的是能出名呀
粉丝一大堆的感觉真爽,还能帮助社区的发展。

启动监听器

在run方法中有下面两句代码

SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();

咱们先看一下getRunListeners
代码如下

  private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
        SpringApplicationRunListener.class, types, this, args));
  }

上面的代码还是通过getSpringFactoriesInstances这个方法去Spring.factories配置文件拉取监听器通过反射实例化对象
该处实例化的class是EventPublishingRunListener,事件发布监听器
接着就是将它Starting
继续看下代码

  @Override
  public void starting() {
    this.initialMulticaster.multicastEvent(
        //关键代码,这里是创建application启动事件ApplicationStartingEvent
        new ApplicationStartingEvent(this.application, this.args));
  }

其中有这么一个方法multicastEvent看名字就知道了事件传播
是否还记得咱们第一章里面说到的SpringApplication初始化的时候加载的一大堆监听器
就是这里通过Event类型,然后invoke调用的。
这里用到的设计模式了就是观察者模式,这堆监听器里面具体做了啥我也没怎么看就不写了。

获取环境

  private ConfigurableEnvironment prepareEnvironment(
      SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments) {
    // Create and configure the environment
    //获取对应的ConfigurableEnvironment
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    //配置property,以及profiles
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    //发布环境已准备事件,这是第二次发布事件
    listeners.environmentPrepared(environment);
    bindToSpringApplication(environment);
    if (this.webApplicationType == WebApplicationType.NONE) {
      environment = new EnvironmentConverter(getClassLoader())
          .convertToStandardEnvironmentIfNecessary(environment);
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
  }

  private ConfigurableEnvironment getOrCreateEnvironment() {
  //根据不同web类型获取不同的环境class类,在这里会获取到系统参数以及启动参数比如spring.profiles.acitve
  //构造函数在abstract里面调用,然后子类重写了父类方法
    if (this.environment != null) {
      return this.environment;
    }
    if (this.webApplicationType == WebApplicationType.SERVLET) {
      return new StandardServletEnvironment();
    }
    return new StandardEnvironment();
  }


到上面咱们都还没有获取到应用程序的配置文件主要实现在事件里面,还是通过钩子去发布一个ApplicationEnvironmentPreparedEvent事件
然后起调ConfigFileApplicationListener中的onApplicationEnvironmentPreparedEvent方法


 private void onApplicationEnvironmentPreparedEvent(
     ApplicationEnvironmentPreparedEvent event) {
   //去spring.factories中加载处理流程
   List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
   //将自己也加入进去DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";这个下面最后再加载一次
   postProcessors.add(this);
   AnnotationAwareOrderComparator.sort(postProcessors);
   for (EnvironmentPostProcessor postProcessor : postProcessors) {
     postProcessor.postProcessEnvironment(event.getEnvironment(),
         event.getSpringApplication());
   }
 }

到这就加载到了所有的环境信息

创建容器

//根据不同的应用类型加载不同的web ApplicationContext也就是大家常听到的BeanFactory,记住不是FactoryBean。这两个完全不是同一个东西!
//由于是一个web应用加载这AnnotationConfigServletWebServerApplicationContext
  protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
      try {
        switch (this.webApplicationType) {
        case SERVLET:
          contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
          break;
        case REACTIVE:
          contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
          break;
        default:
          contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
        }
      }
      catch (ClassNotFoundException ex) {
        throw new IllegalStateException(
            "Unable create a default ApplicationContext, "
                + "please specify an ApplicationContextClass",
            ex);
      }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
  }

准备容器

  private void prepareContext(ConfigurableApplicationContext context,
      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments, Banner printedBanner) {
    //设置环境到容器
    context.setEnvironment(environment);
    //执行前置处理,前置后置处理器是运行在bean初始化的前后
    postProcessApplicationContext(context);
    //执行容器中的ApplicationContextInitializer(包括 spring.factories和自定义的实例)
    applyInitializers(context);
    //发布一个容器准备就绪事件
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
    }

    // Add boot specific singleton beans
    context.getBeanFactory().registerSingleton("springApplicationArguments",
        applicationArguments);
    if (printedBanner != null) {
      context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
    }

    // Load the sources
    //获取我们的启动类指定的参数,可以是多个
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    //加载我们的启动类,将启动类注入容器
    load(context, sources.toArray(new Object[0]));
    //发布容器已加载事件
    listeners.contextLoaded(context);
  }

这里的执行顺序是before initialize after

刷新容器

  private void refreshContext(ConfigurableApplicationContext context) {
    //调用AbstractApplicationContext
    refresh(context);
    if (this.registerShutdownHook) {
      try {
        context.registerShutdownHook();
      }
      catch (AccessControlException ex) {
        // Not allowed in some environments.
      }
    }
  }

这里实际会进入AbstractApplicationContext中去执行refresh方法,这就和常见的springFrame基本一样了
如果不清楚调用过程,可以通过idea看一下AnnotationConfigServletWebServerApplicationContext
这个类的继承结构类图

刷新容器后扩展

这里是一个预留的扩展接口啥都没干,可以自己实现

到这里咱们又过了一遍启动流程,后面再具体说bean注册实例化自动配置怎么实现的。

欢迎扫码加入知识星球继续讨论
avatar

你可能感兴趣的:(SpringBoot2启动流程深入分析)