溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

[SpringBoot]源碼分析SpringBoot的異常處理機(jī)制

發(fā)布時(shí)間:2020-06-28 21:04:09 來(lái)源:網(wǎng)絡(luò) 閱讀:5210 作者:GitShare 欄目:軟件技術(shù)

微信號(hào):GitShare
微信公眾號(hào):愛(ài)折騰的稻草
如有問(wèn)題或建議,請(qǐng)?jiān)诠娞?hào)留言[1]

前續(xù)

為幫助廣大SpringBoot用戶(hù)達(dá)到“知其然,更需知其所以然”的境界,作者將通過(guò)SpringBoot系列文章全方位對(duì)SpringBoot2.0.0.RELEASE版本深入分解剖析,讓您深刻的理解其內(nèi)部工作原理。

正文

在SpringBoot啟動(dòng)時(shí),會(huì)查找并加載所有可用的SpringBootExceptionReporter,其源碼如下:

//7 使用SpringFactoriesLoader在應(yīng)用的classpath中查找并加載所有可用的SpringBootExceptionReporter
exceptionReporters = getSpringFactoriesInstances(
        SpringBootExceptionReporter.class,
        new Class[] { ConfigurableApplicationContext.class }, context);

繼續(xù)查看getSpringFactoriesInstances方法源碼:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
        Class<?>[] parameterTypes, Object... args)
 
{
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    //查找并加載classpath路徑下META-INF/spring.factories中配置的SpringBootExceptionReporter的所有名稱(chēng)
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    //實(shí)例化所有的SpringBootExceptionReporter
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    //排序
    AnnotationAwareOrderComparator.sort(instances);
    //返回結(jié)果
    return instances;
}

代碼來(lái)看不難,也是通過(guò)Spring的Factories機(jī)制來(lái)加載,之前的文章中已經(jīng)詳細(xì)講解過(guò)其過(guò)程。

SpringBootExceptionReporter
@FunctionalInterface
public interface SpringBootExceptionReporter {

    /**
     * 向用戶(hù)報(bào)告啟動(dòng)失敗。
     */

    boolean reportException(Throwable failure);

}
  • SpringBootExceptionReporter是一個(gè)回調(diào)接口,用于支持對(duì)SpringApplication啟動(dòng)錯(cuò)誤的自定義報(bào)告。
    里面就一個(gè)報(bào)告啟動(dòng)失敗的方法。

  • 其實(shí)現(xiàn)類(lèi):org.springframework.boot.diagnostics.FailureAnalyzers  
    用于觸發(fā)從spring.factories加載的FailureAnalyzer和FailureAnalysisReporter實(shí)例。

FailureAnalyzers
1、實(shí)例化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);
}
  • 獲取類(lèi)加載器

  • 加載并實(shí)例化所有的FailureAnalyzer  
    通過(guò)Spring的Factories機(jī)制來(lái)查找和加載所有的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
  • 準(zhǔn)備所有的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());
    }
}

準(zhǔn)備階段:根據(jù)FailureAnalyzer的類(lèi)型,設(shè)置其BeanFactory或者Environment屬性值。

2、FailureAnalyzer
@FunctionalInterface
public interface FailureAnalyzer {

    /**
     * 返回給定故障的分析,如果不無(wú)法分析,則返回null。
     */

    FailureAnalysis analyze(Throwable failure);

}

用于分析故障并提供可以顯示給用戶(hù)的診斷信息。

3、AbstractFailureAnalyzer

FailureAnalyzer的抽象基類(lèi),是個(gè)泛型類(lèi),泛型參數(shù)為T(mén)hrowable的子類(lèi).其實(shí)現(xiàn)了analyze方法,源碼如下:

@Override
public FailureAnalysis analyze(Throwable failure) {
    // 1. 獲得failure中的異常堆棧中是type類(lèi)型的異常
    T cause = findCause(failure, getCauseType());
    if (cause != null) {
        // 2. 如果不等于null,則進(jìn)行分析
        return analyze(failure, cause);
    }
    // 3. 無(wú)法分析,則返回null
    return null;
}
  • 獲得failure中的異常堆棧中是type類(lèi)型的異常。

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的具體實(shí)現(xiàn)  

3.1、AbstractInjectionFailureAnalyzer:用來(lái)對(duì)注入異常進(jìn)行分析的抽象基類(lèi)。    
3.2、BeanCurrentlyInCreationFailureAnalyzer:針對(duì)BeanCurrentlyInCreationException.對(duì)BeanCurrentlyInCreationException(循環(huán)依賴(lài))進(jìn)行分析。    
3.3、BeanNotOfRequiredTypeFailureAnalyzer:針對(duì)BeanNotOfRequiredTypeException異常進(jìn)行分析。  
3.4、BindFailureAnalyzer:針對(duì)BindException異常進(jìn)行分析。  
3.5、BindValidationFailureAnalyzer :針對(duì)BindValidationException或者BindException異常進(jìn)行分析。
3.6、ConnectorStartFailureAnalyzer:針對(duì)ConnectorStartFailedException(tomcat端口占用時(shí)拋出)異常進(jìn)行分析。  
3.7、DataSourceBeanCreationFailureAnalyzer:針對(duì)DataSourceBeanCreationException異常進(jìn)行分析。  
3.8、HikariDriverConfigurationFailureAnalyzer:它對(duì)使用不支持的“dataSourceClassName”屬性導(dǎo)致的Hikari配置失敗進(jìn)行分析。  
3.9、InvalidConfigurationPropertyNameFailureAnalyzer:針對(duì)InvalidConfigurationPropertyNameException異常進(jìn)行分析。  
3.10、InvalidConfigurationPropertyValueFailureAnalyzer:針對(duì)InvalidConfigurationPropertyValueException異常進(jìn)行分析。  
3.11、NoUniqueBeanDefinitionFailureAnalyzer:針對(duì)NoUniqueBeanDefinitionException異常進(jìn)行分析,且實(shí)現(xiàn)了BeanFactoryAware接口。  
3.12、PortInUseFailureAnalyzer:針對(duì)PortInUseException(jetty,undertow 容器啟動(dòng)時(shí)端口占用時(shí)拋出)異常進(jìn)行分析。
3.13、UnboundConfigurationPropertyFailureAnalyzer:針對(duì)BindException異常進(jìn)行分析。  
3.14、ValidationExceptionFailureAnalyzer:泛型參數(shù)為ValidationException(當(dāng)使用validation相關(guān)的注解,但是沒(méi)有加入相關(guān)實(shí)現(xiàn)時(shí)觸發(fā),一般不容易觸發(fā),因?yàn)橐坏┘尤雜pring-boot-starter-web依賴(lài),就會(huì)加入hibernate-validator)。
4、失敗報(bào)告(FailureAnalysisReporter)
@FunctionalInterface
public interface FailureAnalysisReporter {

    /**
     * 將失敗結(jié)果(failureAnalysis)報(bào)告給用戶(hù)
     */

    void report(FailureAnalysis analysis);

}
  • 失敗結(jié)果報(bào)告接口,將失敗結(jié)果信息報(bào)告給用戶(hù)

  • 其實(shí)現(xiàn)類(lèi):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();
    }

}

通過(guò)日志的方式進(jìn)行打印失敗錯(cuò)誤信息。

小結(jié)

Spring的代碼使用了很多設(shè)計(jì)模式,所以閱讀起來(lái)總是繞來(lái)繞去,個(gè)人感覺(jué)其可讀性比較差。今天原本是想畫(huà)出異常處理機(jī)制的相關(guān)類(lèi)圖,但是發(fā)現(xiàn)這里的接口和類(lèi)的關(guān)系都比較簡(jiǎn)單,所以就偷懶。如果您通過(guò)本文講解,還不是很清晰的話(huà),您可以將其類(lèi)圖畫(huà)出來(lái),那樣會(huì)幫助您理解。

后記

為幫助廣大SpringBoot用戶(hù)達(dá)到“知其然,更需知其所以然”的境界,作者將通過(guò)SpringBoot系列文章全方位對(duì)SpringBoot2.0.0.RELEASE版本深入分解剖析,讓您深刻的理解其內(nèi)部工作原理。

本系列歷史文章列表
  • 1、[SpringBoot]利用SpringBoot快速構(gòu)建并啟動(dòng)項(xiàng)目

  • 2、[SpringBoot]詳解SpringBoot應(yīng)用的啟動(dòng)過(guò)程

  • 3、[SpringBoot]深入淺出剖析SpringBoot的應(yīng)用類(lèi)型識(shí)別機(jī)制

  • 4、[SpringBoot]深入淺出剖析SpringBoot中Spring Factories機(jī)制

  • 5、[SpringBoot]詳解SpringBoot中SpringApplication的run方法的前三步

  • 6、[SpringBoot]圖解Spring的Environment機(jī)制

  • 7、[SpringBoot]源碼解析SpringBoot應(yīng)用Environment的構(gòu)造過(guò)程

  • 8、[SpringBoot]源碼解析SpringBoot的Banner機(jī)制

  • 9、[SpringBoot]圖解SpringBoot的應(yīng)用上下文機(jī)制



[SpringBoot]源碼分析SpringBoot的異常處理機(jī)制


向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI