SpringBoot核心特性——自定义启动失败分析

快速启动

创建一个SpringBoot工程,笔者本次学习的SpirngBoot版本为 2.3.1.RELEASE

org.springframework.boot
spring-boot-starter-parent
2.3.1.RELEASE

添加相关依赖

org.projectlombok
lombok
1.16.18
org.springframework.boot
spring-boot-starter-web

编写简单的启动类

package geek.springboot.application;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

启动成功,Tomcat默认监听8080端口

image.png

自定义启动失败分析

有时候因为某些原因会导致SpringApplication启动失败,此时可以往SpringBoot中注册自定义失败分析器来进行具体的分析和处理。

1. 实现org.springframework.boot.diagnostics.FailureAnalyzer接口
  • 该接口只有一个analyze方法,当发生错误时,SpirngBoot将会遍历已经注册的FailureAnalyzer接口实现类,并调用其analyze方法。若该FailureAnalyzer实现可以对该异常的分析,则返回一个FailureAnalysis实例,否则需要返回null,好让下一个FailureAnalyzer进行analyze。
  • image.png

  • 创建一个CustomFailureAnalyzer类,实现 org.springframework.boot.diagnostics.FailureAnalyzer 接口
  • package geek.springboot.application.failureAnalyzer;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.boot.diagnostics.FailureAnalysis;
    import org.springframework.boot.diagnostics.FailureAnalyzer;
    /**
    * 自定义错误分析
    * 通过实现{@link FailureAnalyzer}接口
    * @author Bruse
    */
    @Slf4j
    public class CustomFailureAnalyzer implements FailureAnalyzer {
    @Override
    public FailureAnalysis analyze(Throwable failure) {
    log.error("错误 {}", failure.getMessage());
    // 构造一个FailureAnalysis并返回,传参分别是 描述 、措施 、 原因
    return new FailureAnalysis(failure.getMessage(), "Please Chang Server Port", failure);
    }
    }
  • 在项目resources文件夹下新建META-INF/spring.factories文件,注册CustomFailureAnalyzer
  • org.springframework.boot.diagnostics.FailureAnalyzer=geek.springboot.application.failureAnalyzer.CustomFailureAnalyzer
    
  • 笔者事先已让8080端口被占用,在没有修改server port 的情况下,尝试启动Application,产生端口已被占用的冲突,启动失败,CustomFailureAnalyzer的analyze()方法被调用,控制台输出如下,说明自定义FailureAnalyzer配置成功。
  • image.png

    2.继承 org.springframework.boot.diagnostics.AbstractFailureAnalyzer 类
  • 创建CustomFailureAnalyzerByExtend类,继承自AbstractFailureAnalyzer,
  • package geek.springboot.application.failureAnalyzer;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
    import org.springframework.boot.diagnostics.FailureAnalysis;
    import org.springframework.context.ApplicationContextException;
    /**
    * 自定义错误分析
    * 通过继承 {@link org.springframework.boot.diagnostics.AbstractFailureAnalyzer}
    *
    * @author Bruse
    */
    @Slf4j
    public class CustomFailureAnalyzerByExtend extends AbstractFailureAnalyzer {
    @Override
    protected FailureAnalysis analyze(Throwable rootFailure, ApplicationContextException cause) {
    log.error("CustomFailureAnalyzerByExtend Error: {}", cause.getMessage());
    return new FailureAnalysis(cause.getMessage(), " You Need To Change Port", cause);
    }
    }
  • 修改spring.factories,注册CustomFailureAnalyzerByExtend类
  • org.springframework.boot.diagnostics.FailureAnalyzer=
    geek.springboot.application.failureAnalyzer.CustomFailureAnalyzerByExtend
  • 再次尝试启动, CustomFailureAnalyzerByExtend实例的analyze()方法被调用,控制台输出如下,说明自定义AbstractFailureAnalyzer配置成功。
  • image.png

    AbstractFailureAnalyzer 和 FailureAnalyzer 的区别

    AbstractFailureAnalyzer抽象类其实也实现了FailureAnalyzer接口,只是对于要处理的异常更有针对性,即在继承AbstractFailureAnalyzer时,子类可以特别指定哪些异常由其自身进行处理。

    AbstractFailureAnalyzer源码部分:

    image.png

    image.png

    通过analyze()方法不难看出,父类其实会先判断一下实际产生的异常是否匹配泛型中指定的要处理的异常,只有两者匹配,才会真正调用子类实现的analyze()方法进行实际分析。

    内置的FailureAnalyzer

    SpringBoot默认内置了几种FailureAnalyzer相关实现。以ConnectorStartFailureAnalyzer为例,该类继承自AbstractFailureAnalyzer,并且泛型指定为ConnectorStartFailedException,表示该类实例仅关注ConnectorStartFailedException,仅会对该异常进行尝试分析。

    ConnectorStartFailureAnalyzer源码部分:

    image.png

    失败分析报告

    以端口占用导致Application启动失败为例,控制台输出如下

    image.png

    可以看到在没有注册自定义的AbstractFailureAnalyzer或FailureAnalyzer的默认情况下,信息输出是交给LoggingFailureAnalysisReporter进行输出的。该类仅实现了FailureAnalysisReporter接口,通过实现该接口的report()方法,进行相关信息输出。

    源码片段如下:

    image.png

    image.png

    当然了,FailureAnalysisReporter也是通过Spring.factories进行注册的

    image.png

    继续深究挖出幕后BOOS—— FailureAnalyzers 类,该类主要职责其实就是调用众多FailureAnalyzer进行analyze并最终report给用户的逻辑。

    以下是源码片段:

    image.png

    image.png

    整体逻辑非常清楚,就是将所有FailureAnalyzer实例进行遍历,逐个调用该analyze()方法,获取到分析结果analysis后,调用所有FailureAnalysisReporter实例的report()方法进行报告。