SpringBoot之自定義啟動(dòng)異常堆棧信息打印方式
在SpringBoot項(xiàng)目啟動(dòng)過(guò)程中,當(dāng)一些配置或者其他錯(cuò)誤信息會(huì)有一些的規(guī)范的提示信息
***************************
APPLICATION FAILED TO START
***************************Description:
Web server failed to start. Port 8080 was already in use.
Action:
Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.
在SpringBoot 中其實(shí)現(xiàn)原理是什么,我們?cè)撊绾巫远x異常信息呢
1、SpringBoot異常處理的源碼分析
在springboot啟動(dòng)的核心方法run中會(huì)加載所有的SpringBootExceptionReporter
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context);
調(diào)用了getSpringFactoriesInstances方法
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
其主要通過(guò)Spring的Factories機(jī)制來(lái)加載
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { //異常捕獲中,向用戶(hù)打印異常信息 handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }
在try catch中,catch會(huì)打印異常信息
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception, Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) { try { try { handleExitCode(context, exception); if (listeners != null) { listeners.failed(context, exception); } } finally { reportFailure(exceptionReporters, exception); if (context != null) { context.close(); } } } catch (Exception ex) { logger.warn("Unable to close ApplicationContext", ex); } ReflectionUtils.rethrowRuntimeException(exception); }
private void reportFailure(Collection<SpringBootExceptionReporter> exceptionReporters, Throwable failure) { try { for (SpringBootExceptionReporter reporter : exceptionReporters) { if (reporter.reportException(failure)) { registerLoggedException(failure); return; } } } catch (Throwable ex) { // Continue with normal handling of the original failure } if (logger.isErrorEnabled()) { logger.error("Application run failed", failure); registerLoggedException(failure); } }
遍歷exceptionReporters,打印日常信息
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í)例。
2、如何自定義異常信息
/** * <p> * * <p> * * @author: xuwd * @time: 2020/11/16 10:52 */ public class WannaStopException extends RuntimeException { }
自定義異常信息打印
public class StopFailureAnalyzer extends AbstractFailureAnalyzer<WannaStopException> { @Override protected FailureAnalysis analyze(Throwable rootFailure, WannaStopException cause) { for (StackTraceElement stackTraceElement : cause.getStackTrace()) { if (stackTraceElement.getClassName().equals("com.pigx.demo.Config21")) { return new FailureAnalysis("A想停止", "別要A了", cause); } } return null; } }
接下來(lái)令他生效,通過(guò)上面分析可以可看出需要通過(guò)AutoConfigurationImportSelector,類(lèi)似于自定義SpringBoot Starter AutoConfiguration的形式,我們需要在META-INF/spring.factories文件內(nèi)進(jìn)行定義,如下所示:
接著在合適的地方拋出WannaStopException 異常
總結(jié)
在springboot 啟動(dòng)過(guò)程中會(huì)先對(duì)異常信息進(jìn)行補(bǔ)捕獲,對(duì)進(jìn)行日志格式處理的日志進(jìn)行處理;其核心是通過(guò)SpringBootExceptionReporter回調(diào)及sping-spi bean的管理。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
spring中@Autowire和@Resource的區(qū)別在哪里(推薦)
這篇文章主要介紹了spring中@Autowire和@Resource的區(qū)別在哪里?本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-02-02springboot中如何配置LocalDateTime JSON返回時(shí)間戳
這篇文章主要介紹了springboot中如何配置LocalDateTime JSON返回時(shí)間戳問(wèn)題。具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06java統(tǒng)計(jì)漢字字?jǐn)?shù)的方法示例
這篇文章主要介紹了java統(tǒng)計(jì)漢字字?jǐn)?shù)的方法,結(jié)合實(shí)例形式分析了java正則判定、字符串遍歷及統(tǒng)計(jì)相關(guān)操作技巧,需要的朋友可以參考下2017-05-05SpringBoot入門(mén)實(shí)現(xiàn)第一個(gè)SpringBoot項(xiàng)目
今天我們一起來(lái)完成一個(gè)簡(jiǎn)單的SpringBoot(Hello World)。就把他作為你的第一個(gè)SpringBoot項(xiàng)目。具有一定的參考價(jià)值,感興趣的可以了解一下2021-09-09springboot docker原理及項(xiàng)目構(gòu)建
這篇文章主要介紹了springboot docker原理及項(xiàng)目構(gòu)建,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11SpringBoot2.6.x默認(rèn)禁用循環(huán)依賴(lài)后的問(wèn)題解決
由于SpringBoot從底層逐漸引導(dǎo)開(kāi)發(fā)者書(shū)寫(xiě)規(guī)范的代碼,同時(shí)也是個(gè)憂(yōu)傷的消息,循環(huán)依賴(lài)的應(yīng)用場(chǎng)景實(shí)在是太廣泛了,所以SpringBoot 2.6.x不推薦使用循環(huán)依賴(lài),本文給大家說(shuō)下SpringBoot2.6.x默認(rèn)禁用循環(huán)依賴(lài)后的應(yīng)對(duì)策略,感興趣的朋友一起看看吧2022-02-02解決java.util.NoSuchElementException異常正確方法
java.util.NoSuchElementException是Java中的一種異常,表示在迭代器或枚舉中找不到元素,這篇文章主要給大家介紹了關(guān)于解決java.util.NoSuchElementException異常的相關(guān)資料,需要的朋友可以參考下2023-11-11教你通過(guò)B+Tree平衡多叉樹(shù)理解InnoDB引擎的聚集和非聚集索引
大家都知道B+Tree是從二叉樹(shù)演化而來(lái),在這之前我們來(lái)先了解二叉樹(shù)、平衡二叉樹(shù)、平衡多叉樹(shù),這篇文章主要介紹了通過(guò)B+Tree平衡多叉樹(shù)理解InnoDB引擎的聚集和非聚集索引,需要的朋友可以參考下2022-01-01Java TreeMap升序|降序排列和按照value進(jìn)行排序的案例
這篇文章主要介紹了Java TreeMap升序|降序排列和按照value進(jìn)行排序的案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10SpringBoot利用jackson格式化時(shí)間的三種方法
日常開(kāi)發(fā)過(guò)程中經(jīng)常會(huì)使用json進(jìn)行數(shù)據(jù)的傳輸,這就涉及到了對(duì)象和json的相互轉(zhuǎn)化,常用的解決方案有:Jackson(推薦)、谷歌的Gson、阿里的Fastjson,這篇文章主要給大家介紹了關(guān)于SpringBoot如何利用jackson格式化時(shí)間的相關(guān)資料,需要的朋友可以參考下2021-06-06