[SpringBoot]源碼分析SpringBoot的異常處理機制
微信號:GitShare
微信公眾號:愛折騰的稻草
如有問題或建議,請在公眾號留言[1]
前續
為幫助廣大SpringBoot用戶達到“知其然,更需知其所以然”的境界,作者將通過SpringBoot系列文章全方位對SpringBoot2.0.0.RELEASE版本深入分解剖析,讓您深刻的理解其內部工作原理。
正文
在SpringBoot啟動時,會查找並加載所有可用的SpringBootExceptionReporter,其源碼如下:
//7 使用SpringFactoriesLoader在應用的classpath中查找並加載所有可用的SpringBootExceptionReporter
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
繼續查看getSpringFactoriesInstances方法源碼:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//查找並加載classpath路徑下META-INF/spring.factories中配置的SpringBootExceptionReporter的所有名稱
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//實例化所有的SpringBootExceptionReporter
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//排序
AnnotationAwareOrderComparator.sort(instances);
//返回結果
return instances;
}
代碼來看不難,也是通過Spring的Factories機制來加載,之前的文章中已經詳細講解過其過程。
SpringBootExceptionReporter
@FunctionalInterface
public interface SpringBootExceptionReporter {
/**
* 向用戶報告啟動失敗。
*/
boolean reportException(Throwable failure);
}
SpringBootExceptionReporter是一個回調接口,用於支持對SpringApplication啟動錯誤的自定義報告。
裏面就一個報告啟動失敗的方法。其實現類:org.springframework.boot.diagnostics.FailureAnalyzers
用於觸發從spring.factories加載的FailureAnalyzer和FailureAnalysisReporter實例。
FailureAnalyzers
1、實例化FailureAnalyzers
FailureAnalyzers(ConfigurableApplicationContext context) {
this(context, null);
}
FailureAnalyzers(ConfigurableApplicationContext context, ClassLoader classLoader) {
Assert.notNull(context, "Context must not be null");
this.classLoader = (classLoader == null ? context.getClassLoader() : classLoader);
this.analyzers = loadFailureAnalyzers(this.classLoader);
prepareFailureAnalyzers(this.analyzers, context);
}
獲取類加載器
加載並實例化所有的FailureAnalyzer
通過Spring的Factories機制來查找和加載所有的FailureAnalyzer,
加載/META/spring.factories 中的org.springframework.boot.diagnostics.FailureAnalyzer配置如下:
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
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.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer
準備所有的FailureAnalyzer
private void prepareAnalyzer(ConfigurableApplicationContext context,
FailureAnalyzer analyzer) {
if (analyzer instanceof BeanFactoryAware) {
((BeanFactoryAware) analyzer).setBeanFactory(context.getBeanFactory());
}
if (analyzer instanceof EnvironmentAware) {
((EnvironmentAware) analyzer).setEnvironment(context.getEnvironment());
}
}
準備階段:根據FailureAnalyzer的類型,設置其BeanFactory或者Environment屬性值。
2、FailureAnalyzer
@FunctionalInterface
public interface FailureAnalyzer {
/**
* 返回給定故障的分析,如果不無法分析,則返回null。
*/
FailureAnalysis analyze(Throwable failure);
}
用於分析故障並提供可以顯示給用戶的診斷信息。
3、AbstractFailureAnalyzer
FailureAnalyzer的抽象基類,是個泛型類,泛型參數為Throwable的子類.其實現了analyze方法,源碼如下:
@Override
public FailureAnalysis analyze(Throwable failure) {
// 1. 獲得failure中的異常堆棧中是type類型的異常
T cause = findCause(failure, getCauseType());
if (cause != null) {
// 2. 如果不等於null,則進行分析
return analyze(failure, cause);
}
// 3. 無法分析,則返回null
return null;
}
獲得failure中的異常堆棧中是type類型的異常。
protected final <E extends Throwable> E findCause(Throwable failure, Class<E> type) {
while (failure != null) {
if (type.isInstance(failure)) {
return (E) failure;
}
failure = failure.getCause();
}
return null;
}
AbstractFailureAnalyzer的具體實現
3.1、AbstractInjectionFailureAnalyzer:用來對註入異常進行分析的抽象基類。
3.2、BeanCurrentlyInCreationFailureAnalyzer:針對BeanCurrentlyInCreationException.對BeanCurrentlyInCreationException(循環依賴)進行分析。
3.3、BeanNotOfRequiredTypeFailureAnalyzer:針對BeanNotOfRequiredTypeException異常進行分析。
3.4、BindFailureAnalyzer:針對BindException異常進行分析。
3.5、BindValidationFailureAnalyzer :針對BindValidationException或者BindException異常進行分析。
3.6、ConnectorStartFailureAnalyzer:針對ConnectorStartFailedException(tomcat端口占用時拋出)異常進行分析。
3.7、DataSourceBeanCreationFailureAnalyzer:針對DataSourceBeanCreationException異常進行分析。
3.8、HikariDriverConfigurationFailureAnalyzer:它對使用不支持的“dataSourceClassName”屬性導致的Hikari配置失敗進行分析。
3.9、InvalidConfigurationPropertyNameFailureAnalyzer:針對InvalidConfigurationPropertyNameException異常進行分析。
3.10、InvalidConfigurationPropertyValueFailureAnalyzer:針對InvalidConfigurationPropertyValueException異常進行分析。
3.11、NoUniqueBeanDefinitionFailureAnalyzer:針對NoUniqueBeanDefinitionException異常進行分析,且實現了BeanFactoryAware接口。
3.12、PortInUseFailureAnalyzer:針對PortInUseException(jetty,undertow 容器啟動時端口占用時拋出)異常進行分析。
3.13、UnboundConfigurationPropertyFailureAnalyzer:針對BindException異常進行分析。
3.14、ValidationExceptionFailureAnalyzer:泛型參數為ValidationException(當使用validation相關的註解,但是沒有加入相關實現時觸發,一般不容易觸發,因為一旦加入spring-boot-starter-web依賴,就會加入hibernate-validator)。
4、失敗報告(FailureAnalysisReporter)
@FunctionalInterface
public interface FailureAnalysisReporter {
/**
* 將失敗結果(failureAnalysis)報告給用戶
*/
void report(FailureAnalysis analysis);
}
失敗結果報告接口,將失敗結果信息報告給用戶
其實現類:LoggingFailureAnalysisReporter
public final class LoggingFailureAnalysisReporter implements FailureAnalysisReporter {
private static final Log logger = LogFactory
.getLog(LoggingFailureAnalysisReporter.class);
@Override
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));
}
}
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的代碼使用了很多設計模式,所以閱讀起來總是繞來繞去,個人感覺其可讀性比較差。今天原本是想畫出異常處理機制的相關類圖,但是發現這裏的接口和類的關系都比較簡單,所以就偷懶。如果您通過本文講解,還不是很清晰的話,您可以將其類圖畫出來,那樣會幫助您理解。
後記
為幫助廣大SpringBoot用戶達到“知其然,更需知其所以然”的境界,作者將通過SpringBoot系列文章全方位對SpringBoot2.0.0.RELEASE版本深入分解剖析,讓您深刻的理解其內部工作原理。
本系列歷史文章列表
1、[SpringBoot]利用SpringBoot快速構建並啟動項目
2、[SpringBoot]詳解SpringBoot應用的啟動過程
3、[SpringBoot]深入淺出剖析SpringBoot的應用類型識別機制
4、[SpringBoot]深入淺出剖析SpringBoot中Spring Factories機制
5、[SpringBoot]詳解SpringBoot中SpringApplication的run方法的前三步
6、[SpringBoot]圖解Spring的Environment機制
7、[SpringBoot]源碼解析SpringBoot應用Environment的構造過程
8、[SpringBoot]源碼解析SpringBoot的Banner機制
9、[SpringBoot]圖解SpringBoot的應用上下文機制
[SpringBoot]源碼分析SpringBoot的異常處理機制