本Spring Boot版本是2.2.2

1、流程

1.1、 初始化流程

在run 方法中,将所有的方法都放在了try块中,而在catch 块中处理所有的异常和错误(Throwable )

(1) 创建 SpringBootExceptionReporter 实例
从spring.factories中获取 SpringBootExceptionReporter 类型的类的全类名,实例化这些类

(2)创建 FailureAnlyzer 实例
从从spring.factories中获取 FailureAnlyzer 类型的类的全类名,实例化这些类并放入 SpringBootExceptionReporter 实例的List实例变量中

1.2、调用流程

2、源码

public ConfigurableApplicationContext run(String... args) {...Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();...try {...// 从spring.factories 文件中获取并创建所有 SpringBootExceptionReporter 类型实例exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);...}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;}

2.1、创建 SpringBootExceptionReporter 实例

1、从spring.factories 中解析出全类名(SpringBootExceptionReporter类型的类的)

之前分析过,这里略…

2、创建这些类的实例,其中有FailureAnalyzers(目前SpringBoot中SpringBootExceptionReporter接口只有这个一个实现类),FailureAnalyzers构造方法如下:

FailureAnalyzers(ConfigurableApplicationContext context, ClassLoader classLoader) {Assert.notNull(context, "Context must not be null");this.classLoader = (classLoader != null) ? classLoader : context.getClassLoader();this.analyzers = loadFailureAnalyzers(this.classLoader);prepareFailureAnalyzers(this.analyzers, context);
}

FailureAnalyzers 构造方法做了两件事情

(2.1)解析,创建,排序 ,即从spring.factories中解析所有 FailureAnalyzer 类型的类全名,并创建这些类的实例

private List<FailureAnalyzer> loadFailureAnalyzers(ClassLoader classLoader) {List<String> analyzerNames = SpringFactoriesLoader.loadFactoryNames(FailureAnalyzer.class, classLoader);List<FailureAnalyzer> analyzers = new ArrayList<>();for (String analyzerName : analyzerNames) {try {Constructor<?> constructor = ClassUtils.forName(analyzerName, classLoader).getDeclaredConstructor();ReflectionUtils.makeAccessible(constructor);analyzers.add((FailureAnalyzer) constructor.newInstance());}catch (Throwable ex) {logger.trace(LogMessage.format("Failed to load %s", analyzerName), ex);}}AnnotationAwareOrderComparator.sort(analyzers);return analyzers;
}

笔者环境解析结果

analyzers = {ArrayList@3363}  size = 180 = {BeanCurrentlyInCreationFailureAnalyzer@2918} 1 = {BeanDefinitionOverrideFailureAnalyzer@3402} 2 = {BeanNotOfRequiredTypeFailureAnalyzer@3403} 3 = {BindFailureAnalyzer@3404} 4 = {BindValidationFailureAnalyzer@3405} 5 = {UnboundConfigurationPropertyFailureAnalyzer@3406} 6 = {ConnectorStartFailureAnalyzer@3407} 7 = {NoSuchMethodFailureAnalyzer@3408} 8 = {NoUniqueBeanDefinitionFailureAnalyzer@3398} 9 = {PortInUseFailureAnalyzer@3409} 10 = {ValidationExceptionFailureAnalyzer@3410} 11 = {InvalidConfigurationPropertyNameFailureAnalyzer@3411} 12 = {InvalidConfigurationPropertyValueFailureAnalyzer@3412} 13 = {NoSuchBeanDefinitionFailureAnalyzer@3413} 14 = {FlywayMigrationScriptMissingFailureAnalyzer@3414} 15 = {DataSourceBeanCreationFailureAnalyzer@3415} 16 = {HikariDriverConfigurationFailureAnalyzer@3416} 17 = {NonUniqueSessionRepositoryFailureAnalyzer@3417}

这些类都是 FailureAnalyzer 接口的实现类,我们来看看 FailureAnalyzer 接口

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);
}

我们发现接口中只有 analyze 方法,方法返回类型是 FailureAnalysis ,FailureAnalysis 用来封装失败信息的,下面是FailureAnalysis 类的定义:

public class FailureAnalysis {private final String description;// 错误描述private final String action;// 处理失败的操作private final Throwable cause;// 失败原因
}

下面是使用同一端口第二次启动应用的报错信息,大家看看 Description 和 Action 的描述感受一下

Description:Web server failed to start. Port 8008 was already in use.
Action:Identify and stop the process that's listening on port 8008 or configure this application to listen on another port.

(2.2)为BeanFactoryAware 或 EnvironmentAware类型的 FailureAnalyzer 实例,注入BeanFactory 或 Environment

private void prepareFailureAnalyzers(List<FailureAnalyzer> analyzers, ConfigurableApplicationContext context) {for (FailureAnalyzer analyzer : analyzers) {prepareAnalyzer(context, analyzer);}
}private void prepareAnalyzer(ConfigurableApplicationContext context, FailureAnalyzer analyzer) {if (analyzer instanceof BeanFactoryAware) {((BeanFactoryAware) analyzer).setBeanFactory(context.getBeanFactory());}if (analyzer instanceof EnvironmentAware) {((EnvironmentAware) analyzer).setEnvironment(context.getEnvironment());}
}

FailureAnalyzer 类型的实例有:

1、BeanFactoryAwareNoUniqueBeanDefinitionFailureAnalyzerNoSuchBeanDefinitionFailureAnalyzer
2、EnvironmentAwareInvalidConfigurationPropertyValueFailureAnalyzerDataSourceBeanCreationFailureAnalyzer

2.2、处理异常

当程序发生异常时,在 catch 块中处理这个异常

private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {try {try {// 1、handleExitCode(context, exception);if (listeners != null) {// 2、listeners.failed(context, exception);}}finally {// 3、reportFailure(exceptionReporters, exception);if (context != null) {// 4、context.close();}}}catch (Exception ex) {logger.warn("Unable to close ApplicationContext", ex);}ReflectionUtils.rethrowRuntimeException(exception);
}

发生端口占用异常时:

exception = {PortInUseException@5268} "org.springframework.boot.web.server.PortInUseException: Port 8008 is already in use"$assertionsDisabled = truecause = nullCAUSE_CAPTION = "Caused by: "detailMessage = "Port 8008 is already in use"EMPTY_THROWABLE_ARRAY = {Throwable[0]@5295} Exception.serialVersionUID = -3387516993124229948NULL_CAUSE_MESSAGE = "Cannot suppress a null exception."port = 8008SELF_SUPPRESSION_MESSAGE = "Self-suppression not permitted"serialVersionUID = -7034897190745766939stackTrace = {StackTraceElement[10]@5300} SUPPRESSED_CAPTION = "Suppressed: "SUPPRESSED_SENTINEL = {Collections$UnmodifiableRandomAccessList@5302}  size = 0suppressedExceptions = {Collections$UnmodifiableRandomAccessList@5302}  size = 0Throwable.serialVersionUID = -3042686055658047285UNASSIGNED_STACK = {StackTraceElement[0]@5298}

(1)、


/********** org.springframework.boot.SpringApplication ******************/
private void handleExitCode(ConfigurableApplicationContext context, Throwable exception) {int exitCode = getExitCodeFromException(context, exception);if (exitCode != 0) {if (context != null) {context.publishEvent(new ExitCodeEvent(context, exitCode));}SpringBootExceptionHandler handler = getSpringBootExceptionHandler();if (handler != null) {handler.registerExitCode(exitCode);}}
}
private int getExitCodeFromException(ConfigurableApplicationContext context, Throwable exception) {int exitCode = getExitCodeFromMappedException(context, exception);if (exitCode == 0) {exitCode = getExitCodeFromExitCodeGeneratorException(exception);}return exitCode;
}
private int getExitCodeFromMappedException(ConfigurableApplicationContext context, Throwable exception) {if (context == null || !context.isActive()) {return 0;}ExitCodeGenerators generators = new ExitCodeGenerators();Collection<ExitCodeExceptionMapper> beans // 空的= context.getBeansOfType(ExitCodeExceptionMapper.class).values();generators.addAll(exception, beans);// exception 就是 PortInUseException实例return generators.getExitCode();
}
private int getExitCodeFromExitCodeGeneratorException(Throwable exception) {if (exception == null) {return 0;}if (exception instanceof ExitCodeGenerator) {return ((ExitCodeGenerator) exception).getExitCode();}return getExitCodeFromExitCodeGeneratorException(exception.getCause());
}/********** org.springframework.boot.ExitCodeGenerators ******************/
void addAll(Throwable exception, Iterable<? extends ExitCodeExceptionMapper> mappers) {Assert.notNull(exception, "Exception must not be null");Assert.notNull(mappers, "Mappers must not be null");for (ExitCodeExceptionMapper mapper : mappers) {add(exception, mapper);}
}
void add(Throwable exception, ExitCodeExceptionMapper mapper) {Assert.notNull(exception, "Exception must not be null");Assert.notNull(mapper, "Mapper must not be null");add(new MappedExitCodeGenerator(exception, mapper));
}
void add(ExitCodeGenerator generator) {Assert.notNull(generator, "Generator must not be null");this.generators.add(generator);
}int getExitCode() {int exitCode = 0;for (ExitCodeGenerator generator : this.generators) {try {int value = generator.getExitCode();if (value > 0 && value > exitCode || value < 0 && value < exitCode) {exitCode = value;}}catch (Exception ex) {exitCode = (exitCode != 0) ? exitCode : 1;ex.printStackTrace();}}return exitCode;
}

(2)、

(3)、报告异常

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);}
}
private FailureAnalysis analyze(Throwable failure, List<FailureAnalyzer> analyzers) {for (FailureAnalyzer analyzer : analyzers) {try {FailureAnalysis analysis = analyzer.analyze(failure);if (analysis != null) {return analysis;}}catch (Throwable ex) {logger.debug(LogMessage.format("FailureAnalyzer %s failed", analyzer), ex);}}return null;
}
protected Class<? extends T> getCauseType() {return (Class<? extends T>) ResolvableType.forClass(AbstractFailureAnalyzer.class, getClass()).resolveGeneric();
}

(4)、

2.7、Spring Boot 异常处理体系相关推荐

  1. Spring Boot异常处理

    一.默认映射 我们在做Web应用的时候,请求处理过程中发生错误是非常常见的情况.Spring Boot提供了一个默认的映射:/error,当处理中抛出异常之后,会转到该请求中处理,并且该请求有一个全局 ...

  2. spring boot 异常处理

    spring boot在异常的处理中,默认实现了一个EmbeddedServletContainerCustomizer并定义了一个错误页面到"/error"中,在ErrorMvc ...

  3. SpringBoot - Spring Boot 中的配置体系Profile全面解读

    文章目录 Pre Spring Boot 中的配置体系 配置文件与 Profile 主 application.properties 中指定激活的Profile Profile 配置信息只保存在一个文 ...

  4. Spring Boot进阶:原理、实战与面试题分析

    在当下的互联网应用中,业务体系日益复杂,业务功能也在不断地变化.以典型的电商类应用为例,其背后的业务功能复杂度以及快速迭代要求的开发速度,与5年前的同类业务系统相比,面临着诸多新的挑战. 这些挑战中核 ...

  5. 手慢无,阿里云神作被《Spring Boot进阶原理实战》成功扒下,限时

    又来给大家分享好书了:郑天民老师的 <Spring Boot进阶:原理.实战与面试题分析>,别问网上有没有开源版本!问就是我也不知道,哈哈!但我会有 郑天民是谁? 资深架构师和技术专家,有 ...

  6. [201903][Spring Boot 编程思想][核心篇][小马哥][著]

    [201903][Spring Boot 编程思想][核心篇][小马哥][著] The Java Community Process(SM) Program https://jcp.org/en/ho ...

  7. spring boot快速入门

    1.什么是SpringBoot 一个javaweb的开发框架,和SpringMVC类似,对比其他javaweb框架的好处,官方说是简化开发,约定大于配置, you can "just run ...

  8. 拍案叫绝!阿里内部开源Spring Boot实战+进阶+面试攻略手慢无

    近年来,伴随着应用程序升级改造,企业对敏捷开发技术的需求愈加迫切,其中Spring Boot快速开发框架已经成为核心技术,是每一名Java工程师必须掌握的技能. 攻略覆盖了Spring Boot的核心 ...

  9. Spring Boot统一异常处理的拦截指南

    通常我们在Spring Boot中设置的统一异常处理只能处理Controller抛出的异常.有些请求还没到Controller就出异常了,而这些异常不能被统一异常捕获,例如Servlet容器的某些异常 ...

最新文章

  1. textarea怎么占满整个td_怎么知道网上的视频是不是专业摆拍?关注这些细节就可以了...
  2. 【复杂系统迁移 .NET Core平台系列】之认证和授权
  3. MFC 最详细入门教程
  4. 数据结构折半查找例题_数据结构第9章例题与答案
  5. Java案例:词频统计
  6. C语言:求1到100之间的所有素数,并设定每行输出5个素数
  7. OpenCV颜色空间——HLS颜色空间
  8. web前端简易制作之HTML
  9. bootmgr is missing错误:首先检查系统分区是否为活动分区
  10. DialogFragment自动弹出软键盘,消失时关闭软键盘
  11. 微信群里的这几类人,真让人想打他
  12. 二、Linux 文件与目录结构、VI/VIM 编辑器(重要)
  13. 灰狼优化matlab,混合灰狼优化(HGWO,DE-GWO)算法matlab源码
  14. 新媒体运营:内容运营的核心与技巧
  15. 经典日内策略——空中花园(附源码)
  16. 访问服务器上视频的文件,服务器上的视频文件
  17. linux 蓝牙模块,蓝牙模块在HHARM2410上的移植
  18. 使用Promise.race()实现控制并发
  19. S2B2C社交电商系统怎么开发?
  20. PS_高低频和中性灰——双曲线

热门文章

  1. [Ext JS 4] 实战之Grid, Tree Gird编辑Cell
  2. 安卓手机通话记录路径_一点换机小技巧,轻松迁移旧手机资料到新手机
  3. 关于结构体的浅拷贝和深拷贝
  4. 串口接收到的字符型数据如何转化成数字
  5. 自定义springSecurity3.0 登录后自定义返回页面
  6. Spring Data Jpa 报错org.hibernate.LazyInitializationException: could not initialize proxy
  7. Oracle之表示约束状态的关键字Enable/Disable/Validate/Novalidate
  8. 关闭tcp服务器_2020年2月起,不支持TCP和UDP协议的DNS服务器将被强制关闭
  9. three.js中文文档下载_threejs基础学习一
  10. apache+mysql+wordpress的安装建站