前言
一个Spring Boot 应用偶尔会因为某些原因启动失败,此时Spring Boot会友好地输出类似于这样一段文字,告诉你发生了什么,甚至应该采取什么行动:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.example.B required a bean of type 'com.example.A' that could not be found.
Action:
Consider defining a bean of type 'com.example.A' in your configuration.
这是怎么做到的呢?或许第一想法是SpringBoot会在出现问题的地方构造这样完整的异常和输出。实际上并非如此,Spring Boot提供了统一的接口进行问题的分析和诊断,这就是org.springframework.boot.diagnostics.FailureAnalyzer
接口。
本文所使用的源码版本为 2.2.2.RELEASE,如有出入请检查版本是否不一致。
从哪开始
已知Spring Boot项目是通过调用SpringApplication#run(java.lang.String...)
启动的。我们会发现当启动过程抛出异常时,是这样处理的:
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
// ...
}
观察一下找到方法参数中exceptionReporters
就是用来报告处理异常的,回过头来寻找其定义,发现其通过SpringFactoriesLoader#loadFactoryNames
加载classpath下META-INF中spring.factories里定义的SpringBootExceptionReporter
实现类。
一般只有FailureAnalyzers
这一个实现类,里面的处理也很简洁:通过SpringFactoriesLoader
加载到所有定义的FailureAnalyzer
实现,然后稍微prepare一下。分析异常时遍历所有的FailureAnalyzer
看谁能分析到问题就好了。分析完呢还要报告分析结果,又是加载了FailureAnalysisReporter
所有实现类,然后一个一个报告。通常也只有一个,就是我们在“前言”中看到输出日志的LoggingFailureAnalysisReporter
。
SpringFactoriesLoader 类似于Java原生的SPI,可以通过编写配置文件为某个接口寻找服务实现。
做一个自己的
如果做一个自己的组件,就可能会遇上需要处理异常并向使用者提供建议的情况。
我们定义一个异常
public class WannaStopException extends RuntimeException {
}
我们定义一个Bean在某(全)种(部)情况下会抛出异常
@Service
public class A {
public A() {
throw new WannaStopException();
}
}
我们定义一个FailureAnalyzer
专门处理这个WannaStopException
public class StopFailureAnalyzer extends AbstractFailureAnalyzer<WannaStopException> {
@Override
protected FailureAnalysis analyze(Throwable rootFailure, WannaStopException cause) {
for (StackTraceElement stackTraceElement : cause.getStackTrace()) {
if (stackTraceElement.getClassName().equals("com.example.A")) {
return new FailureAnalysis("A想停止", "别要A了", cause);
}
}
return null;
}
}
AbstractFailureAnalyzer 帮助我们找到特定异常
接下来我们还要把StopFailureAnalyzer
放进spring.factories
中,在resources/META-INF/spring.factories(自己建)里添加
org.springframework.boot.diagnostics.FailureAnalyzer=
com.example.flowable.StopFailureAnalyzer
启动这个应用,就能看到如下输出
***************************
APPLICATION FAILED TO START
***************************
Description:
A想停止
Action:
别要A了
另外也注意到FailureAnalysisReporter
可以用来向开发进行报警,或者进行一些自动修复操作(如自动回滚)等。