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)文章
SpringSecurity實(shí)現(xiàn)圖形驗(yàn)證碼功能的實(shí)例代碼
Spring Security 的前身是 Acegi Security ,是 Spring 項(xiàng)目組中用來(lái)提供安全認(rèn)證服務(wù)的框架。這篇文章主要介紹了SpringSecurity實(shí)現(xiàn)圖形驗(yàn)證碼功能,需要的朋友可以參考下2018-10-10
關(guān)于Java Interface接口的簡(jiǎn)單練習(xí)題
這篇文章主要給大家分享的是關(guān)于Java Interface接口的簡(jiǎn)單練習(xí)題,難度不算大,但是要有一個(gè)清晰的邏輯建立接口和鏈接Java類(lèi)。下面來(lái)看看文章的詳細(xì)介紹吧,需要的朋友可以參考一下2021-11-11
springMVC之HandlerExceptionResolver使用
這篇文章主要介紹了springMVC之HandlerExceptionResolver使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11
MybatisPlusInterceptor實(shí)現(xiàn)sql攔截器超詳細(xì)教程
這篇文章主要給大家介紹了關(guān)于MybatisPlusInterceptor實(shí)現(xiàn)sql攔截器超詳細(xì)教程的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-08-08
詳解Java多線程編程中LockSupport類(lèi)的線程阻塞用法
LockSupport類(lèi)提供了park()和unpark()兩個(gè)方法來(lái)實(shí)現(xiàn)線程的阻塞和喚醒,下面我們就來(lái)詳解Java多線程編程中LockSupport類(lèi)的線程阻塞用法:2016-07-07
詳解Spring Data JPA系列之投影(Projection)的用法
本篇文章主要介紹了詳解Spring Data JPA系列之投影(Projection)的用法,具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07
Java虛擬機(jī)之對(duì)象創(chuàng)建過(guò)程與類(lèi)加載機(jī)制及雙親委派模型
這篇文章主要給大家介紹了關(guān)于Java虛擬機(jī)之對(duì)象創(chuàng)建過(guò)程與類(lèi)加載機(jī)制及雙親委派模型的相關(guān)資料,本文通過(guò)示例代碼以及圖文介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2021-11-11
Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(22)
下面小編就為大家?guī)?lái)一篇Java基礎(chǔ)的幾道練習(xí)題(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,希望可以幫到你2021-07-07

