SpringBoot 在项目启动时如果遇到异常并不能友好的打印出具体的堆栈错误信息 ,我们只能查看到简单的错误消息,以致于并不能及时解决发生的问题,针对这个问题SpringBoot 提供了故障分析仪的概念(failure-analyzer),内部根据不同类型的异常提供了一些实现,我们如果想自定义该怎么去做?
FailureAnalyzer
SpringBoot提供了启动异常分析接口FailureAnalyzer ,该接口位于org.springframework.boot.diagnostics package内。内部仅提供一个分析的方法,源码如下所示:
@FunctionalInterface public interface FailureAnalyzer {
该接口会把遇到的异常对象实例Throwable failure 交付给实现类,实现类进行自定义处理。
AbstractFailureAnalyzer
AbstractFailureAnalyzer 是FailureAnalyzer 的基础实现抽象类,实现了FailureAnalyzer 定义的analyze(Throwable failure) 方法,并提供了一个指定异常类型的抽象方法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; }
通过AbstractFailureAnalyzer 源码我们可以看到,它在实现于FailureAnalyzer 的接口方法内进行了特殊处理,根据getCauseType() 方法获取当前类定义的第一个泛型类型,也就是我们需要分析的指定异常类型 。
获取泛型异常类型后根据方法findCause 判断Throwable 是否与泛型异常类型匹配,如果匹配直接返回给SpringBoot 进行注册处理。
SpringBoot提供的分析实现
SpringBoot 内部通过实现AbstractFailureAnalyzer 抽象类定义了一系列的针对性异常类型的启动分析,如下图所示:
springboot-failure-analyzer.png
指定异常分析
SpringBoot 内部提供的启动异常分析都是指定具体的异常类型实现的,最常见的一个错误就是端口号被占用(PortInUseException ),虽然SpringBoot 内部提供一个这个异常的启动分析,我们也是可以进行替换这一异常分析的,我们只需要创建PortInUseException 异常的AbstractFailureAnalyzer ,并且实现类注册给SpringBoot 即可,实现自定义如下所示:
注册启动异常分析
在上面我们只是编写了指定异常启动分析,我们接下来需要让它生效,这个生效方式比较特殊,类似于自定义SpringBoot Starter AutoConfiguration 的形式,我们需要在META-INF/spring.factories 文件内进行定义,如下所示:
org.springframework.boot.diagnostics.FailureAnalyzer=\ org.minbox.chapter.springboot.failure.analyzer.PortInUseFailureAnalyzer
那我们为什么需要使用这种方式定义呢?
项目启动遇到的异常顺序不能确定,很可能在Spring IOC 并未执行初始化之前就出现了异常,我们不能通过@Component 注解的形式使其生效,所以SpringBoot 提供了通过spring.factories 配置文件的方式定义。
启动异常分析继承关系
自定义的运行异常一般都是继承自RuntimeException ,如果我们定义一个RuntimeException 的异常启动分析实例会是什么效果呢?
将该类也一并注册到spring.factories 文件内,如下所示:
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.minbox.chapter.springboot.failure.analyzer.PortInUseFailureAnalyzer,\
org.minbox.chapter.springboot.failure.analyzer.ProjectBootUnifiedFailureAnalyzer
运行项目并测试端口号被占用异常 我们会发现,并没有执行ProjectBootUnifiedFailureAnalyzer 内的analyze 方法,而是继续执行了PortInUseFailureAnalyzer 类内的方法。
那我们将PortInUseFailureAnalyzer 这个启动分析从spring.factories 文件内暂时删除掉 ,再来运行项目我们会发现这时却是会执行ProjectBootUnifiedFailureAnalyzer 类内分析方法。
总结
根据本章我们了解了SpringBoot 提供的启动异常分析接口以及基本抽象实现类的运作原理,而且启动异常分析存在分析泛型异常类的上下级继承关系,异常子类的启动分析会覆盖掉异常父类的启动分析,如果你想包含全部异常的启动分析可以尝试使用Exception 作为AbstractFailureAnalyzer 的泛型参数。
|