SpringBoot源码解析(六)容器刷新后处理

991 阅读1分钟
public ConfigurableApplicationContext run(String... args) {
   //1 创建StopWatch对象
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   //2 设置Headless模式
   configureHeadlessProperty();
   //3 创建事件发布器
   SpringApplicationRunListeners listeners = getRunListeners(args);
   listeners.starting();
   try {
      // 将main方法的args参数封装到一个对象中
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
      //4 准备容器环境信息
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
      configureIgnoreBeanInfo(environment);
      //5.打印Banner
      Banner printedBanner = printBanner(environment);
      //6.创建IOC容器和Bean工厂
      context = createApplicationContext();
      //7.初始化异常报告器
      exceptionReporters = getSpringFactoriesInstances(
            SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
      //8.初始化IOC容器
      prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
      //9.刷新容器----》Spring源码解析
      refreshContext(context);
      //10.刷新后的处理
      afterRefresh(context, applicationArguments);
      //11.关闭计时器
      stopWatch.stop();
      if (this.logStartupInfo) {
         //12.打印启动信息
         new StartupInfoLogger(this.mainApplicationClass)
               .logStarted(getApplicationLog(), stopWatch);
      }
      //13.发布容器启动事件
      listeners.started(context);
      //14.回调运行器
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }

   try {
      //15.发布容器启动完成事件
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

十、afterRefresh:刷新后的处理

这里是留给子类的扩展,在容器刷新后可以实现拓展逻辑

protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}

十一、stopWatch.stop():关闭计时器

这里会记录SpringBoot启动花费了多长时间

public void stop() throws IllegalStateException {
   if (this.currentTaskName == null) {
      throw new IllegalStateException("Can't stop StopWatch: it's not running");
   }
   //记录启动花费了多长时间
   long lastTime = System.nanoTime() - this.startTimeNanos;
   //总花费时间
   this.totalTimeNanos += lastTime;
   //将启动花费时间记录放入任务中
   this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);
   if (this.keepTaskList) {
      //将任务添加进集合
      this.taskList.add(this.lastTaskInfo);
   }
   ++this.taskCount;
   this.currentTaskName = null;
}

十二、打印启动信息

StartupInfoLogger会打印出SpringBoot启动时间(在计时器中保存)和JVM启动时间

private CharSequence getStartedMessage(StopWatch stopWatch) {
   StringBuilder message = new StringBuilder();
   message.append("Started ");
   appendApplicationName(message);
   message.append(" in ");
   message.append(stopWatch.getTotalTimeMillis() / 1000.0);
   message.append(" seconds");
   try {
      double uptime = ManagementFactory.getRuntimeMXBean().getUptime() / 1000.0;
      message.append(" (JVM running for ").append(uptime).append(")");
   }
   catch (Throwable ex) {
      // No JVM time available
   }
   return message;
}

十三、发布容器启动事件

void started(ConfigurableApplicationContext context) {
   for (SpringApplicationRunListener listener : this.listeners) {
      listener.started(context);
   }
}

这里会进入EventPublishingRunListener中发布容器启动事件ApplicationStartedEvent

public void started(ConfigurableApplicationContext context) {
   context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
   AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
}

十四、回调运行器

获取容器中ApplicationRunner和CommandLineRunner实现类回调callRunner方法

private void callRunners(ApplicationContext context, ApplicationArguments args) {
   List<Object> runners = new ArrayList<>();
   runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
   runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
   AnnotationAwareOrderComparator.sort(runners);
   for (Object runner : new LinkedHashSet<>(runners)) {
      if (runner instanceof ApplicationRunner) {
         callRunner((ApplicationRunner) runner, args);
      }
      if (runner instanceof CommandLineRunner) {
         callRunner((CommandLineRunner) runner, args);
      }
   }
}

十五、发布容器启动完成事件

这里会进入EventPublishingRunListener中发布容器启动完成事件ApplicationReadyEvent

@Override
public void running(ConfigurableApplicationContext context) {
   context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
   AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
}

总结

SpringBoot在容器启动完成后就没有做什么处理,都是预留拓展点。

  1. 子类实现预留方法:afterRefresh
  2. 打印启动信息(容器启动时间、JVM运行时间)
  3. 发布ApplicationStartedEvent和ApplicationReadyEvent事件
  4. 回调运行器ApplicationRunner和CommandLineRunner