前言
一個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
可以用來向開發進行報警,或者進行一些自動修復操作(如自動回滾)等。