spring boot 源码解析10-SpringApplication#run第10-13步

前言

这篇我们开始解析SpringApplication#run第10步-13步.本文分两步来说明:

  1. SpringApplication#run的第10-12步–> 代表着spring boot 的正常启动处理
  2. SpringApplication#run的第13步–>代表着Spring boot 在run方法中出现了异常.此时又分两种情况:

    1. 在Spring boot 中的ApplicationContext的激活之前抛出异常(也就是在Spring boot run 方法的前9步抛出异常)
    2. 在Spring boot 中的ApplicationContext的激活之后抛出异常(也就是在Spring boot run 方法的10步之后抛出异常)

Spring boot 第10-12步

  1. 第10步,执行SpringApplication#afterRefresh,代码如下:

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

    调用

    private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList<Object>();
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners);
        for (Object runner : new LinkedHashSet<Object>(runners)) {
            if (runner instanceof ApplicationRunner) {
                callRunner((ApplicationRunner) runner, args);
            }
            if (runner instanceof CommandLineRunner) {
                callRunner((CommandLineRunner) runner, args);
            }
        }
    }
    1. 将context中ApplicationRunner,CommandLineRunner 类型的bean添加到runners后进行排序. 当前没有实现类.
    2. 遍历runners

      1. 如果是ApplicationRunner的话,则执行如下代码:

        private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
        try {
        (runner).run(args);
        }
        catch (Exception ex) {
        throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
        }
        }
      2. 如果是CommandLineRunner的话,则执行如下代码:

        private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
        try {
            (runner).run(args.getSourceArgs());
        }
        catch (Exception ex) {
            throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
        }
        }
  2. 第11步,最终会调用EventPublishingRunListener#finished,代码如下:

        public void finished(ConfigurableApplicationContext context, Throwable exception) {
        SpringApplicationEvent event = getFinishedEvent(context, exception);
        if (context != null && context.isActive()) {
            // Listeners have been registered to the application context so we should
            // use it at this point if we can
            context.publishEvent(event);
        }
        else {
            // An inactive context may not have a multicaster so we use our multicaster to
            // call all of the context's listeners instead
            if (context instanceof AbstractApplicationContext) {
                for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
                        .getApplicationListeners()) {
                    this.initialMulticaster.addApplicationListener(listener);
                }
            }
            if (event instanceof ApplicationFailedEvent) {
                this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
            }
            this.initialMulticaster.multicastEvent(event);
        }
    }
    
    
    1. 获得SpringApplicationEvent,代码如下:

      private SpringApplicationEvent getFinishedEvent(
          ConfigurableApplicationContext context, Throwable exception) {
      if (exception != null) {
          return new ApplicationFailedEvent(this.application, this.args, context,
                  exception);
      }
      return new ApplicationReadyEvent(this.application, this.args, context);
      }

      如果在启动过程中有异常的话,则返回ApplicationFailedEvent,否则返回ApplicationReadyEvent.注意,由于此时是正常启动,因此会返回ApplicationReadyEvent

    2. 如果context 不等于null并且context 是激活状态,则进行发布

    3. 否则

      1. 如果context是 AbstractApplicationContext 实例的话,则首先向initialMulticaster添加ApplicationListeners
      2. 如果event为ApplicationFailedEvent,则设置ErrorHandler 为LoggingErrorHandler
      3. 发送事件

    正常情况下,都会执行第2步,最终调用SimpleApplicationEventMulticaster#multicastEvent进行发布,这个我们之前有分析过,对该ApplicationReadyEvent感兴趣的ApplicationListener有:

    org.springframework.boot.autoconfigure.BackgroundPreinitializer,
    org.springframework.boot.context.config.DelegatingApplicationListener,

    BackgroundPreinitializer#onApplicationEvent 将调用preinitializationComplete#await 将CountDownLatch 减一,以将在ApplicationEnvironmentPreparedEvent中启动的线程 处理完毕.

    DelegatingApplicationListener 空实现

    注意,如果我们在配置文件配置有spring.application.admin.enabled=true,则SpringApplicationAdminMXBeanRegistrar也会对ApplicationReadyEvent事件作出处理.其
    onApplicationEvent方法实现如下:

    public void onApplicationEvent(ApplicationReadyEvent event) {
        if (this.applicationContext.equals(event.getApplicationContext())) {
            this.ready = true;
        }
    }

    判断ApplicationReadyEvent 中的applicationContext 是否是自己的持有的,如果是的话,则说明自己持有的applicationContext已经初始化完毕,将ready设置为true即可.

  3. 第12步,停止计时.代码如下:

        public void stop() throws IllegalStateException {
        if (!this.running) {
            throw new IllegalStateException("Can't stop StopWatch: it's not running");
        }
        long lastTime = System.currentTimeMillis() - this.startTimeMillis;
        this.totalTimeMillis += lastTime;
        this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);
        if (this.keepTaskList) {
            this.taskList.add(lastTaskInfo);
        }
        ++this.taskCount;
        this.running = false;
        this.currentTaskName = null;
    }

    接下来,打印启动日志

        if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }

    调用

    public void logStarted(Log log, StopWatch stopWatch) {
        if (log.isInfoEnabled()) {
            log.info(getStartedMessage(stopWatch));
        }
    }

    调用getStartedMessages生成日志.

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

    举例: 日志如下:

    2017-12-26 10:02:09.371  INFO 56433 — [ main] com.example.demo.DemoApplication  : Started DemoApplication in 2.156 seconds (JVM running for 2.615)

Spring boot run 方法异常处理

  1. 当SpringApplication#run 运行过程中,发生异常时,会调用handleRunFailure,进行处理,最后抛出IllegalStateException.其中handleRunFailure 代码如下:

        private void handleRunFailure(ConfigurableApplicationContext context,
            SpringApplicationRunListeners listeners, FailureAnalyzers analyzers,
            Throwable exception) {
        try {
            try {
                handleExitCode(context, exception);
                listeners.finished(context, exception);
            }
            finally {
                reportFailure(analyzers, exception);
                if (context != null) {
                    context.close();
                }
            }
        }
        catch (Exception ex) {
            logger.warn("Unable to close ApplicationContext", ex);
        }
        ReflectionUtils.rethrowRuntimeException(exception);
    }
    
    1. 生成ExitCode
    2. 发送ApplicationFailedEvent 事件,由前可知,最终会调用EventPublishingRunListener#finished 进行处理.最终会执行如下代码:

      public void finished(ConfigurableApplicationContext context, Throwable exception) {
          // 1. 获得SpringApplicationEvent
          SpringApplicationEvent event = getFinishedEvent(context, exception);
          // 2.1 如果context 不等于null并且context 是激活状态,则进行发布
          if (context != null && context.isActive()) {
              // Listeners have been registered to the application context so we should
              // use it at this point if we can
              context.publishEvent(event);
          }
          else {
              // An inactive context may not have a multicaster so we use our multicaster to
              // call all of the context's listeners instead
              // 2.2 如果context是 AbstractApplicationContext 实例的话,则首先向initialMulticaster添加ApplicationListeners
              if (context instanceof AbstractApplicationContext) {
                  for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
                          .getApplicationListeners()) {
                      this.initialMulticaster.addApplicationListener(listener);
                  }
              }
              // 2.3 如果event为ApplicationFailedEvent,则设置ErrorHandler 为LoggingErrorHandler
              if (event instanceof ApplicationFailedEvent) {
                  this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
              }
              // 2.4 发送事件
              this.initialMulticaster.multicastEvent(event);
          }
      }
      1. 此时获得的是ApplicationFailedEvent事件
      2. 如果当前的异常是在Spring boot run 方法的10步之后抛出异常,则意味着此时ApplicationContext已经激活了,则直接发送事件.
      3. 如果当前的异常是在Spring boot run 方法的前9步抛出异常,则意味着ApplicationContext没有激活成功,则有可能在ApplicationContext中没有配置ApplicationListener,则此时需要添加ApplicationListener.如果已经配置了,也无妨,因为在AbstractApplicationEventMulticaster中是先删在加的,不会造成重复添加

        同时可以看到设置了ErrorHandler 为LoggingErrorHandler.

      不管是第2步,还是第3步的处理,最终都会发送ApplicationFailedEvent事件.对于该事件感兴趣的ApplicationListener有如下:

      org.springframework.boot.logging.LoggingApplicationListener 
      org.springframework.boot.logging.ClasspathLoggingApplicationListener, 
      org.springframework.boot.autoconfigure.BackgroundPreinitializer, 
      org.springframework.boot.context.config.DelegatingApplicationListener, 
      org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer$AutoConfigurationReportListener

      LoggingApplicationListener#onApplicationEvent 最终会执行如下代码:

      private void onApplicationFailedEvent() {
                  if (this.loggingSystem != null) {
                      this.loggingSystem.cleanUp();
                  }
      }

      很简单,调用LoggingSystem#cleanUp 实现资源的释放.

      ClasspathLoggingApplicationListener#onApplicationEvent,只是打印日志,代码如下:

          public void onApplicationEvent(ApplicationEvent event) {
      if (logger.isDebugEnabled()) {
          if (event instanceof ApplicationEnvironmentPreparedEvent) {
              logger.debug("Application started with classpath: " + getClasspath());
          }
          else if (event instanceof ApplicationFailedEvent) {
              logger.debug(
                      "Application failed to start with classpath: " + getClasspath());
          }
      }
      }

      BackgroundPreinitializer#onApplicationEvent 不管是ApplicationReadyEvent,还是ApplicationFailedEvent,如果preinitializationStarted 为true的话,则意味着其内部的background-preinit 已经启动了,此时需要将preinitializationComplete 这个 CountDownLatch 减一,以让background-preinit 运行完毕,代码如下:

      public void onApplicationEvent(SpringApplicationEvent event) {
      if (event instanceof ApplicationEnvironmentPreparedEvent) {
          if (preinitializationStarted.compareAndSet(false, true)) {
              performPreinitialization();
          }
      }
      if ((event instanceof ApplicationReadyEvent
              || event instanceof ApplicationFailedEvent)
              && preinitializationStarted.get()) {
          try {
              preinitializationComplete.await();
          }
          catch (InterruptedException ex) {
              Thread.currentThread().interrupt();
          }
      }
      }

      DelegatingApplicationListener#onApplicationEvent 没有做任何事.

      AutoConfigurationReportListener#onApplicationEvent 最终会调用如下代码:

      public void logAutoConfigurationReport(boolean isCrashReport) {
      // 1. 如果ConditionEvaluationReport 等于null
      if (this.report == null) {
          // 1.1 如果applicationContext 等于null,则打印日志,return
          if (this.applicationContext == null) {
              this.logger.info("Unable to provide auto-configuration report "
                      + "due to missing ApplicationContext");
              return;
          }
          // 1.2 否则实例化一个
          this.report = ConditionEvaluationReport
                  .get(this.applicationContext.getBeanFactory());
      }
      if (!this.report.getConditionAndOutcomesBySource().isEmpty()) {
          // 2. 如果ConditionAndOutcomesBySource不为空的话
          if (isCrashReport && this.logger.isInfoEnabled()
                  && !this.logger.isDebugEnabled()) {
              // 2.1 如果日志级别为info的话,则打印日志
              this.logger.info(String
                      .format("%n%nError starting ApplicationContext. To display the "
                              + "auto-configuration report re-run your application with "
                              + "'debug' enabled."));
          }
          // 2.2 如果日志为debug的话,则进行输出
          if (this.logger.isDebugEnabled()) {
              this.logger.debug(new ConditionEvaluationReportMessage(this.report));
          }
      }
      }
    3. 打印异常.代码如下:

      private void reportFailure(FailureAnalyzers analyzers, Throwable failure) {
      try {
          if (analyzers != null && analyzers.analyzeAndReport(failure)) {
              registerLoggedException(failure);
              return;
          }
      }
      catch (Throwable ex) {
          // Continue with normal handling of the original failure
      }
      if (logger.isErrorEnabled()) {
          logger.error("Application startup failed", failure);
          registerLoggedException(failure);
      }
      }
      1. 如果analyzers 不为null,并且analyzers#analyzeAndReport返回true的话,则调用registerLoggedException.analyzers#analyzeAndReport代码如下:

        public boolean analyzeAndReport(Throwable failure) {
            FailureAnalysis analysis = analyze(failure, this.analyzers);
            return report(analysis, this.classLoader);
        }
        1. 首先调用analyze 获得FailureAnalysis.代码如下:

          private FailureAnalysis analyze(Throwable failure, List<FailureAnalyzer> analyzers) {
              for (FailureAnalyzer analyzer : analyzers) {
                  try {
                      FailureAnalysis analysis = analyzer.analyze(failure);
                      if (analysis != null) {
                          return analysis;
                      }
                  }
                  catch (Throwable ex) {
                      logger.debug("FailureAnalyzer " + analyzer + " failed", ex);
                  }
              }
              return null;
          }

          通过遍历FailureAnalyzer,如果FailureAnalyzer 能够对该异常进行处理的话,则返回FailureAnalysis. 对于当前情况来说, FailureAnalyzer 如下:

          org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer, 
          org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer, 
          org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer, 
          org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer, 
          org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer, 
          org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer, 
          org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer, 
          org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer, 
          org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer
        2. 调用report,来进行处理.代码如下:

          private boolean report(FailureAnalysis analysis, ClassLoader classLoader) {
              List<FailureAnalysisReporter> reporters = SpringFactoriesLoader
                      .loadFactories(FailureAnalysisReporter.class, classLoader);
              if (analysis == null || reporters.isEmpty()) {
                  return false;
              }
              for (FailureAnalysisReporter reporter : reporters) {
                  reporter.report(analysis);
              }
              return true;
          }
          1. 加载FailureAnalysisReporter.对于当前,只有org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter 一个
          2. 如果FailureAnalysis 不存在 或者 不存在FailureAnalysisReporter 的实例,则直接return.
          3. 遍历FailureAnalysisReporter,调用其report 进行处理.

          LoggingFailureAnalysisReporter#report,代码如下:

          public void report(FailureAnalysis failureAnalysis) {
              if (logger.isDebugEnabled()) {
                  logger.debug("Application failed to start due to an exception",
                          failureAnalysis.getCause());
              }
              if (logger.isErrorEnabled()) {
                  logger.error(buildMessage(failureAnalysis));
              }
          }
          1. 如果日志级别为Debug的话,则打印日志–>Application failed to start due to an exception
          2. 如果日志级别为error的话,则通过调用buildMessage 来生成日志进行打印.代码如下:
          private String buildMessage(FailureAnalysis failureAnalysis) {
              StringBuilder builder = new StringBuilder();
              builder.append(String.format("%n%n"));
              builder.append(String.format("***************************%n"));
              builder.append(String.format("APPLICATION FAILED TO START%n"));
              builder.append(String.format("***************************%n%n"));
              builder.append(String.format("Description:%n%n"));
              builder.append(String.format("%s%n", failureAnalysis.getDescription()));
              if (StringUtils.hasText(failureAnalysis.getAction())) {
                  builder.append(String.format("%nAction:%n%n"));
                  builder.append(String.format("%s%n", failureAnalysis.getAction()));
              }
              return builder.toString();
          }

          举例,假如我们现在出现了循环依赖,则会打印如下日志:

          《spring boot 源码解析10-SpringApplication#run第10-13步》

      2. 如果日志级别为Error的话,则向SpringBootExceptionHandler 注册异常. 代码如下:

        public void registerLoggedException(Throwable exception) {
                this.loggedExceptions.add(exception);
        }
    4. 如果context != null, 则调用其AbstractApplicationContext#close,代码如下:

      public void close() {
          synchronized (this.startupShutdownMonitor) {
              doClose();
              // If we registered a JVM shutdown hook, we don't need it anymore now:
              // We've already explicitly closed the context.
              if (this.shutdownHook != null) {
                  try {
                      Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
                  }
                  catch (IllegalStateException ex) {
                      // ignore - VM is already shutting down
                  }
              }
          }
      }

      首先调用AbstractApplicationContext#doClose,在doClose 判断了只有当active 时才会进行处理.那么何时active会设置为true? 在AbstractApplicationContext#refresh中进行设置.此时区分两种情况:

      1. 如果此时active 为false,则意味是在Spring boot run 方法的前9步抛出异常.此时是不会处理的
      2. 如果此时active 为true,则意味是在Spring boot run 方法的第10步之后抛出异常,此时会发送ContextClosedEvent事件.对于当前,对ContextClosedEvent感兴趣的监听器有:

        LoggingApplicationListener
        DelegatingApplicationListener

        LoggingApplicationListener–> 最终会调用onContextClosedEvent方法,代码如下:

        private void onContextClosedEvent() {
            if (this.loggingSystem != null) {
                this.loggingSystem.cleanUp();
            }
        }

        DelegatingApplicationListener–>空操作

    5. 重新抛出运行时异常.此时注意,如果SpringBootExceptionHandler存在,则最终会调用SpringBootExceptionHandler#uncaughtException方法.代码如下:

      public void uncaughtException(Thread thread, Throwable ex) {
          try {
              if (isPassedToParent(ex) && this.parent != null) {
                  this.parent.uncaughtException(thread, ex);
              }
          }
          finally {
              this.loggedExceptions.clear();
              if (this.exitCode != 0) {
                  System.exit(this.exitCode);
              }
          }
      }
      1. 如果该异常不能处理并且parent不等于null,则调用parent的uncaughtException,默认情况下parent是不等于null的
      2. 清空loggedExceptions
      3. 如果状态码等于0,则退出,状态码为指定的状态码
    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/qq_26000415/article/details/78915234
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞