Spring Boot詳細(xì)打印啟動時異常堆棧信息詳析
前言
SpringBoot在項目啟動時如果遇到異常并不能友好的打印出具體的堆棧錯誤信息,我們只能查看到簡單的錯誤消息,以致于并不能及時解決發(fā)生的問題,針對這個問題SpringBoot提供了故障分析儀的概念(failure-analyzer),內(nèi)部根據(jù)不同類型的異常提供了一些實現(xiàn),我們?nèi)绻胱远x該怎么去做?
FailureAnalyzer
SpringBoot提供了啟動異常分析接口FailureAnalyzer,該接口位于org.springframework.boot.diagnosticspackage內(nèi)。
內(nèi)部僅提供一個分析的方法,源碼如下所示:
@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交付給實現(xiàn)類,實現(xiàn)類進行自定義處理。
AbstractFailureAnalyzer
AbstractFailureAnalyzer是FailureAnalyzer的基礎(chǔ)實現(xiàn)抽象類,實現(xiàn)了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源碼我們可以看到,它在實現(xiàn)于FailureAnalyzer的接口方法內(nèi)進行了特殊處理,根據(jù)getCauseType()方法獲取當(dāng)前類定義的第一個泛型類型,也就是我們需要分析的指定異常類型。
獲取泛型異常類型后根據(jù)方法findCause判斷Throwable是否與泛型異常類型匹配,如果匹配直接返回給SpringBoot進行注冊處理。
SpringBoot提供的分析實現(xiàn)
SpringBoot內(nèi)部通過實現(xiàn)AbstractFailureAnalyzer抽象類定義了一系列的針對性異常類型的啟動分析,如下圖所示:
指定異常分析
SpringBoot內(nèi)部提供的啟動異常分析都是指定具體的異常類型實現(xiàn)的,最常見的一個錯誤就是端口號被占用(PortInUseException),雖然SpringBoot內(nèi)部提供一個這個異常的啟動分析,我們也是可以進行替換這一異常分析的,我們只需要創(chuàng)建PortInUseException異常的AbstractFailureAnalyzer,并且實現(xiàn)類注冊給SpringBoot即可,實現(xiàn)自定義如下所示:
/** * 端口號被占用{@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文件內(nèi)進行定義,如下所示:
org.springframework.boot.diagnostics.FailureAnalyzer=\ org.minbox.chapter.springboot.failure.analyzer.PortInUseFailureAnalyzer
那我們?yōu)槭裁葱枰褂眠@種方式定義呢?
項目啟動遇到的異常順序不能確定,很可能在Spring IOC并未執(zhí)行初始化之前就出現(xiàn)了異常,我們不能通過@Component注解的形式使其生效,所以SpringBoot提供了通過spring.factories配置文件的方式定義。
啟動異常分析繼承關(guān)系
自定義的運行異常一般都是繼承自RuntimeException,如果我們定義一個RuntimeException的異常啟動分析實例會是什么效果呢?
/** * 項目啟動運行時異常{@link RuntimeException}統(tǒng)一啟動分析 * * @author 恒宇少年 */ 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文件內(nèi),如下所示:
org.springframework.boot.diagnostics.FailureAnalyzer=\ org.minbox.chapter.springboot.failure.analyzer.PortInUseFailureAnalyzer,\ org.minbox.chapter.springboot.failure.analyzer.ProjectBootUnifiedFailureAnalyzer
運行項目并測試端口號被占用異常我們會發(fā)現(xiàn),并沒有執(zhí)行ProjectBootUnifiedFailureAnalyzer內(nèi)的analyze方法,而是繼續(xù)執(zhí)行了PortInUseFailureAnalyzer類內(nèi)的方法。
那我們將PortInUseFailureAnalyzer這個啟動分析從spring.factories文件內(nèi)暫時刪除掉,再來運行項目我們會發(fā)現(xiàn)這時卻是會執(zhí)行ProjectBootUnifiedFailureAnalyzer類內(nèi)分析方法。
總結(jié)
根據(jù)本章我們了解了SpringBoot提供的啟動異常分析接口以及基本抽象實現(xiàn)類的運作原理,而且啟動異常分析存在分析泛型異常類的上下級繼承關(guān)系,異常子類的啟動分析會覆蓋掉異常父類的啟動分析,如果你想包含全部異常的啟動分析可以嘗試使用Exception作為AbstractFailureAnalyzer的泛型參數(shù)。
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。
相關(guān)文章
rabbitmq使用springboot實現(xiàn)direct模式(最新推薦)
這篇文章主要介紹了rabbitmq使用springboot實現(xiàn)direct模式,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-07-07Spring Boot + Kotlin整合MyBatis的方法教程
前幾天由于工作需要,便開始學(xué)習(xí)了kotlin,java基礎(chǔ)扎實學(xué)起來也還算比較快,對于kotlin這個編程語言自然是比java有趣一些,下面這篇文章主要給大家介紹了關(guān)于Spring Boot + Kotlin整合MyBatis的方法教程,需要的朋友可以參考下。2018-01-01Java設(shè)計模式之模板模式(Template模式)介紹
這篇文章主要介紹了Java設(shè)計模式之模板模式(Template模式)介紹,定義一個操作中算法的骨架,將一些步驟的執(zhí)行延遲到其子類中,需要的朋友可以參考下2015-03-03Java實現(xiàn)圖片與二進制的互相轉(zhuǎn)換
這篇文章主要為大家詳細(xì)介紹了Java實現(xiàn)圖片與二進制的互相轉(zhuǎn)換,將圖片轉(zhuǎn)二進制再將二進制轉(zhuǎn)成圖片,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-03-03