SpringBoot-技術專區-詳細打印啟動時異常堆棧信息


  SpringBoot在項目啟動時如果遇到異常並不能友好的打印出具體的堆棧錯誤信息,我們只能查看到簡單的錯誤消息,以致於並不能及時解決發生的問題,針對這個問題SpringBoot提供了故障分析儀的概念(failure-analyzer),內部根據不同類型的異常提供了一些實現,我們如果想自定義該怎么去做?

FailureAnalyzer

  SpringBoot提供了啟動異常分析接口FailureAnalyzer,該接口位於org.springframework.boot.diagnosticspackage內。內部僅提供一個分析的方法,源碼如下所示:

@FunctionalInterface public interface FailureAnalyzer { /** * Returns an analysis of the given {@code failure}, or {@code null} if no analysis * was possible. * @param failure the failure * @return the analysis or {@code null} */ FailureAnalysis analyze(Throwable failure); }

  該接口會把遇到的異常對象實例Throwable failure交付給實現類,實現類進行自定義處理。

AbstractFailureAnalyzer

  AbstractFailureAnalyzerFailureAnalyzer的基礎實現抽象類,實現了FailureAnalyzer定義的analyze(Throwable failure)方法,並提供了一個指定異常類型的抽象方法analyze(Throwable rootFailure, T cause),源碼如下所示:


public abstract class AbstractFailureAnalyzer<T extends Throwable> implements FailureAnalyzer { @Override public FailureAnalysis analyze(Throwable failure) { T cause = findCause(failure, getCauseType()); if (cause != null) { return analyze(failure, cause); } return null; } /** * Returns an analysis of the given {@code rootFailure}, or {@code null} if no * analysis was possible. * @param rootFailure the root failure passed to the analyzer * @param cause the actual found cause * @return the analysis or {@code null} */ protected abstract FailureAnalysis analyze(Throwable rootFailure, T cause); /** * Return the cause type being handled by the analyzer. By default the class generic * is used. * @return the cause type */ @SuppressWarnings("unchecked") protected Class<? extends T> getCauseType() { return (Class<? extends T>) ResolvableType.forClass(AbstractFailureAnalyzer.class, getClass()).resolveGeneric(); } @SuppressWarnings("unchecked") 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源碼我們可以看到,它在實現於FailureAnalyzer的接口方法內進行了特殊處理,根據getCauseType()方法獲取當前類定義的第一個泛型類型,也就是我們需要分析的指定異常類型

  獲取泛型異常類型后根據方法findCause判斷Throwable是否與泛型異常類型匹配,如果匹配直接返回給SpringBoot進行注冊處理。

SpringBoot提供的分析實現

SpringBoot內部通過實現AbstractFailureAnalyzer抽象類定義了一系列的針對性異常類型的啟動分析,如下圖所示:

springboot-failure-analyzer.png

指定異常分析

  SpringBoot內部提供的啟動異常分析都是指定具體的異常類型實現的,最常見的一個錯誤就是端口號被占用(PortInUseException),雖然SpringBoot內部提供一個這個異常的啟動分析,我們也是可以進行替換這一異常分析的,我們只需要創建PortInUseException異常的AbstractFailureAnalyzer,並且實現類注冊給SpringBoot即可,實現自定義如下所示:


/** * 端口號被占用{@link PortInUseException}異常啟動分析 * * @author 恆宇少年 */ public class PortInUseFailureAnalyzer extends AbstractFailureAnalyzer<PortInUseException> { /** * logger instance */ static Logger logger = LoggerFactory.getLogger(PortInUseFailureAnalyzer.class); @Override protected FailureAnalysis analyze(Throwable rootFailure, PortInUseException cause) { logger.error("端口被占用。", cause); return new FailureAnalysis("端口號:" + cause.getPort() + "被占用", "PortInUseException", rootFailure); } }

注冊啟動異常分析

       在上面我們只是編寫了指定異常啟動分析,我們接下來需要讓它生效,這個生效方式比較特殊,類似於自定義SpringBoot Starter AutoConfiguration的形式,我們需要在META-INF/spring.factories文件內進行定義,如下所示:

org.springframework.boot.diagnostics.FailureAnalyzer=\ org.minbox.chapter.springboot.failure.analyzer.PortInUseFailureAnalyzer 

那我們為什么需要使用這種方式定義呢?

  項目啟動遇到的異常順序不能確定,很可能在Spring IOC並未執行初始化之前就出現了異常,我們不能通過@Component注解的形式使其生效,所以SpringBoot提供了通過spring.factories配置文件的方式定義。

啟動異常分析繼承關系

  自定義的運行異常一般都是繼承自RuntimeException,如果我們定義一個RuntimeException的異常啟動分析實例會是什么效果呢?

/** * 項目啟動運行時異常{@link RuntimeException}統一啟動分析 */ public class ProjectBootUnifiedFailureAnalyzer extends AbstractFailureAnalyzer<RuntimeException> { /** * logger instance */ static Logger logger = LoggerFactory.getLogger(ProjectBootUnifiedFailureAnalyzer.class); @Override protected FailureAnalysis analyze(Throwable rootFailure, RuntimeException cause) { logger.error("遇到運行時異常", cause); return new FailureAnalysis(cause.getMessage(), "error", rootFailure); } } 

將該類也一並注冊到spring.factories文件內,如下所示:

org.springframework.boot.diagnostics.FailureAnalyzer=\
  org.minbox.chapter.springboot.failure.analyzer.PortInUseFailureAnalyzer,\
  org.minbox.chapter.springboot.failure.analyzer.ProjectBootUnifiedFailureAnalyzer

  

  運行項目並測試端口號被占用異常我們會發現,並沒有執行ProjectBootUnifiedFailureAnalyzer內的analyze方法,而是繼續執行了PortInUseFailureAnalyzer類內的方法。

  那我們將PortInUseFailureAnalyzer這個啟動分析從spring.factories文件內暫時刪除掉,再來運行項目我們會發現這時卻是會執行ProjectBootUnifiedFailureAnalyzer類內分析方法。

總結

  根據本章我們了解了SpringBoot提供的啟動異常分析接口以及基本抽象實現類的運作原理,而且啟動異常分析存在分析泛型異常類的上下級繼承關系,異常子類的啟動分析會覆蓋掉異常父類的啟動分析,如果你想包含全部異常的啟動分析可以嘗試使用Exception作為AbstractFailureAnalyzer的泛型參數。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM