[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> 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版本深入分解剖析,讓您深刻的理解其內部工作原理。

本系列歷史文章列表




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章