1. 程式人生 > >[SpringBoot]源碼分析SpringBoot的異常處理機制

[SpringBoot]源碼分析SpringBoot的異常處理機制

stat file rto ide ror ise cat read 實現類

微信號: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的異常處理機制