前言
这篇我们开始解析SpringApplication#run第10步-13步.本文分两步来说明:
- SpringApplication#run的第10-12步–> 代表着spring boot 的正常启动处理
SpringApplication#run的第13步–>代表着Spring boot 在run方法中出现了异常.此时又分两种情况:
- 在Spring boot 中的ApplicationContext的激活之前抛出异常(也就是在Spring boot run 方法的前9步抛出异常)
- 在Spring boot 中的ApplicationContext的激活之后抛出异常(也就是在Spring boot run 方法的10步之后抛出异常)
Spring boot 第10-12步
第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); } } }
- 将context中ApplicationRunner,CommandLineRunner 类型的bean添加到runners后进行排序. 当前没有实现类.
遍历runners
如果是ApplicationRunner的话,则执行如下代码:
private void callRunner(ApplicationRunner runner, ApplicationArguments args) { try { (runner).run(args); } catch (Exception ex) { throw new IllegalStateException("Failed to execute ApplicationRunner", ex); } }
如果是CommandLineRunner的话,则执行如下代码:
private void callRunner(CommandLineRunner runner, ApplicationArguments args) { try { (runner).run(args.getSourceArgs()); } catch (Exception ex) { throw new IllegalStateException("Failed to execute CommandLineRunner", ex); } }
第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); } }
获得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
如果context 不等于null并且context 是激活状态,则进行发布
否则
- 如果context是 AbstractApplicationContext 实例的话,则首先向initialMulticaster添加ApplicationListeners
- 如果event为ApplicationFailedEvent,则设置ErrorHandler 为LoggingErrorHandler
- 发送事件
正常情况下,都会执行第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即可.
第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; }
举例: 日志如下:
[2m2017-12-26 10:02:09.371[0;39m [32m INFO[0;39m [35m56433[0;39m [2m—[0;39m [2m[ main][0;39m [36mcom.example.demo.DemoApplication [0;39m [2m:[0;39m Started DemoApplication in 2.156 seconds (JVM running for 2.615)
Spring boot run 方法异常处理
当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); }
- 生成ExitCode
发送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); } }
- 此时获得的是ApplicationFailedEvent事件
- 如果当前的异常是在Spring boot run 方法的10步之后抛出异常,则意味着此时ApplicationContext已经激活了,则直接发送事件.
如果当前的异常是在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)); } } }
打印异常.代码如下:
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); } }
如果analyzers 不为null,并且analyzers#analyzeAndReport返回true的话,则调用registerLoggedException.analyzers#analyzeAndReport代码如下:
public boolean analyzeAndReport(Throwable failure) { FailureAnalysis analysis = analyze(failure, this.analyzers); return report(analysis, this.classLoader); }
首先调用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
调用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; }
- 加载FailureAnalysisReporter.对于当前,只有org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter 一个
- 如果FailureAnalysis 不存在 或者 不存在FailureAnalysisReporter 的实例,则直接return.
- 遍历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)); } }
- 如果日志级别为Debug的话,则打印日志–>Application failed to start due to an exception
- 如果日志级别为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(); }
举例,假如我们现在出现了循环依赖,则会打印如下日志:
如果日志级别为Error的话,则向SpringBootExceptionHandler 注册异常. 代码如下:
public void registerLoggedException(Throwable exception) { this.loggedExceptions.add(exception); }
如果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中进行设置.此时区分两种情况:
- 如果此时active 为false,则意味是在Spring boot run 方法的前9步抛出异常.此时是不会处理的
如果此时active 为true,则意味是在Spring boot run 方法的第10步之后抛出异常,此时会发送ContextClosedEvent事件.对于当前,对ContextClosedEvent感兴趣的监听器有:
LoggingApplicationListener DelegatingApplicationListener
LoggingApplicationListener–> 最终会调用onContextClosedEvent方法,代码如下:
private void onContextClosedEvent() { if (this.loggingSystem != null) { this.loggingSystem.cleanUp(); } }
DelegatingApplicationListener–>空操作
重新抛出运行时异常.此时注意,如果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); } } }
- 如果该异常不能处理并且parent不等于null,则调用parent的uncaughtException,默认情况下parent是不等于null的
- 清空loggedExceptions
- 如果状态码等于0,则退出,状态码为指定的状态码