SpringBoot打印啟動(dòng)時(shí)異常堆棧信息詳解
SpringBoot在項(xiàng)目啟動(dòng)時(shí)如果遇到異常并不能友好的打印出具體的堆棧錯(cuò)誤信息,我們只能查看到簡(jiǎn)單的錯(cuò)誤消息,以致于并不能及時(shí)解決發(fā)生的問題,針對(duì)這個(gè)問題SpringBoot提供了故障分析儀的概念(failure-analyzer),內(nèi)部根據(jù)不同類型的異常提供了一些實(shí)現(xiàn),我們?nèi)绻胱远x該怎么去做?
FailureAnalyzer
SpringBoot提供了啟動(dòng)異常分析接口FailureAnalyzer,該接口位于org.springframework.boot.diagnosticspackage內(nèi)。
內(nèi)部?jī)H提供一個(gè)分析的方法,源碼如下所示:
@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);
}
該接口會(huì)把遇到的異常對(duì)象實(shí)例Throwable failure交付給實(shí)現(xiàn)類,實(shí)現(xiàn)類進(jìn)行自定義處理。
AbstractFailureAnalyzer
AbstractFailureAnalyzer是FailureAnalyzer的基礎(chǔ)實(shí)現(xiàn)抽象類,實(shí)現(xiàn)了FailureAnalyzer定義的analyze(Throwable failure)方法,并提供了一個(gè)指定異常類型的抽象方法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源碼我們可以看到,它在實(shí)現(xiàn)于FailureAnalyzer的接口方法內(nèi)進(jìn)行了特殊處理,根據(jù)getCauseType()方法獲取當(dāng)前類定義的第一個(gè)泛型類型,也就是我們需要分析的指定異常類型。
獲取泛型異常類型后根據(jù)方法findCause判斷Throwable是否與泛型異常類型匹配,如果匹配直接返回給SpringBoot進(jìn)行注冊(cè)處理。
SpringBoot提供的分析實(shí)現(xiàn)
SpringBoot內(nèi)部通過實(shí)現(xiàn)AbstractFailureAnalyzer抽象類定義了一系列的針對(duì)性異常類型的啟動(dòng)分析,如下圖所示:

指定異常分析
SpringBoot內(nèi)部提供的啟動(dòng)異常分析都是指定具體的異常類型實(shí)現(xiàn)的,最常見的一個(gè)錯(cuò)誤就是端口號(hào)被占用(PortInUseException),雖然SpringBoot內(nèi)部提供一個(gè)這個(gè)異常的啟動(dòng)分析,我們也是可以進(jìn)行替換這一異常分析的,我們只需要?jiǎng)?chuàng)建PortInUseException異常的AbstractFailureAnalyzer,并且實(shí)現(xiàn)類注冊(cè)給SpringBoot即可,實(shí)現(xiàn)自定義如下所示:
/**
* 端口號(hào)被占用{@link PortInUseException}異常啟動(dòng)分析
*
* @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("端口號(hào):" + cause.getPort() + "被占用", "PortInUseException", rootFailure);
}
}
注冊(cè)啟動(dòng)異常分析
在上面我們只是編寫了指定異常啟動(dòng)分析,我們接下來(lái)需要讓它生效,這個(gè)生效方式比較特殊,類似于自定義SpringBoot Starter AutoConfiguration的形式,我們需要在META-INF/spring.factories文件內(nèi)進(jìn)行定義,如下所示:
org.springframework.boot.diagnostics.FailureAnalyzer=\ org.minbox.chapter.springboot.failure.analyzer.PortInUseFailureAnalyzer
那我們?yōu)槭裁葱枰褂眠@種方式定義呢?
項(xiàng)目啟動(dòng)遇到的異常順序不能確定,很可能在Spring IOC并未執(zhí)行初始化之前就出現(xiàn)了異常,我們不能通過@Component注解的形式使其生效,所以SpringBoot提供了通過spring.factories配置文件的方式定義。
啟動(dòng)異常分析繼承關(guān)系
自定義的運(yùn)行異常一般都是繼承自RuntimeException,如果我們定義一個(gè)RuntimeException的異常啟動(dòng)分析實(shí)例會(huì)是什么效果呢?
/**
* 項(xiàng)目啟動(dòng)運(yùn)行時(shí)異常{@link RuntimeException}統(tǒng)一啟動(dò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("遇到運(yùn)行時(shí)異常", cause);
return new FailureAnalysis(cause.getMessage(), "error", rootFailure);
}
}
將該類也一并注冊(cè)到spring.factories文件內(nèi),如下所示:
org.springframework.boot.diagnostics.FailureAnalyzer=\ org.minbox.chapter.springboot.failure.analyzer.PortInUseFailureAnalyzer,\ org.minbox.chapter.springboot.failure.analyzer.ProjectBootUnifiedFailureAnalyzer
運(yùn)行項(xiàng)目并測(cè)試端口號(hào)被占用異常我們會(huì)發(fā)現(xiàn),并沒有執(zhí)行ProjectBootUnifiedFailureAnalyzer內(nèi)的analyze方法,而是繼續(xù)執(zhí)行了PortInUseFailureAnalyzer類內(nèi)的方法。
那我們將PortInUseFailureAnalyzer這個(gè)啟動(dòng)分析從spring.factories文件內(nèi)暫時(shí)刪除掉,再來(lái)運(yùn)行項(xiàng)目我們會(huì)發(fā)現(xiàn)這時(shí)卻是會(huì)執(zhí)行ProjectBootUnifiedFailureAnalyzer類內(nèi)分析方法。
總結(jié)
根據(jù)本章我們了解了SpringBoot提供的啟動(dòng)異常分析接口以及基本抽象實(shí)現(xiàn)類的運(yùn)作原理,而且啟動(dòng)異常分析存在分析泛型異常類的上下級(jí)繼承關(guān)系,異常子類的啟動(dòng)分析會(huì)覆蓋掉異常父類的啟動(dòng)分析,如果你想包含全部異常的啟動(dòng)分析可以嘗試使用Exception作為AbstractFailureAnalyzer的泛型參數(shù)。
以上就是本次介紹的全部知識(shí)點(diǎn)內(nèi)容,希望腳本之家整理的內(nèi)容能夠幫助到大家。
相關(guān)文章
SkyWalking自定義鏈路追蹤實(shí)現(xiàn)步驟
本文詳細(xì)介紹了如何使用SkyWalking進(jìn)行自定義鏈路追蹤的步驟,包括POM文件依賴和邏輯業(yè)務(wù)代碼的編寫,文章最后推薦了腳本之家作為進(jìn)一步學(xué)習(xí)的資源2024-02-02
Java反射機(jī)制,如何將一個(gè)實(shí)體類所有字段賦值為null
這篇文章主要介紹了Java反射機(jī)制,如何將一個(gè)實(shí)體類所有字段賦值為null,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
如何集成swagger2構(gòu)建Restful API
這篇文章主要介紹了如何集成swagger2構(gòu)建Restful API,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
Java面向?qū)ο蠡A(chǔ)之多態(tài)性,抽象類和接口
這篇文章主要介紹了Java面向?qū)ο蠡A(chǔ):多態(tài)性,抽象類和接口,文中代碼可以幫助各位更好的理解學(xué)習(xí),有需求的小伙伴可以參考下2020-05-05
java 教你如何給你的頭像添加一個(gè)好看的國(guó)旗
這篇文章主要介紹了java 教你如何給你的頭像添加一個(gè)好看的國(guó)旗,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
聊聊spring @Transactional 事務(wù)無(wú)法使用的可能原因
這篇文章主要介紹了spring @Transactional 事務(wù)無(wú)法使用的可能原因,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
java數(shù)據(jù)結(jié)構(gòu)算法稀疏數(shù)組示例詳解
這篇文章主要為大家介紹了java數(shù)據(jù)結(jié)構(gòu)算法稀疏數(shù)組示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06

