在SpringBoot项目启动过程中,当一些配置或者其他错误信息会有一些的规范的提示信息
***************************
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 中其实现原理是什么,我们该如何自定义异常信息呢
1、SpringBoot异常处理的源码分析
在springboot启动的核心方法run中会加载所有的SpringBootExceptionReporter
1
2
|
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter. class , new Class[] { ConfigurableApplicationContext. class }, context); |
调用了getSpringFactoriesInstances方法
1
2
3
4
5
6
7
8
|
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; } |
其主要通过Spring的Factories机制来加载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
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) { //异常捕获中,向用户打印异常信息 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会打印异常信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
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); } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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是一个回调接口,用于支持对SpringApplication启动错误的自定义报告。里面就一个报告启动失败的方法。
其实现类:org.springframework.boot.diagnostics.FailureAnalyzers
用于触发从spring.factories加载的FailureAnalyzer和FailureAnalysisReporter实例。
2、如何自定义异常信息
1
2
3
4
5
6
7
8
9
10
|
/** * <p> * * <p> * * @author: xuwd * @time: 2020/11/16 10:52 */ public class WannaStopException extends RuntimeException { } |
自定义异常信息打印
1
2
3
4
5
6
7
8
9
10
11
12
|
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 ; } } |
接下来令他生效,通过上面分析可以可看出需要通过AutoConfigurationImportSelector,类似于自定义SpringBoot Starter AutoConfiguration的形式,我们需要在META-INF/spring.factories文件内进行定义,如下所示:
接着在合适的地方抛出WannaStopException 异常
总结
在springboot 启动过程中会先对异常信息进行补捕获,对进行日志格式处理的日志进行处理;其核心是通过SpringBootExceptionReporter回调及sping-spi bean的管理。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/weixin_36276193/article/details/109717547