SpringBoot例外処理は知っていました

序文

2020 之前打算将 Spring 和 SpringBoot 的官方文档看一遍,顺便看点感兴趣的源码。

昨天莫名想研究在 SpringBoot 中 Exception 怎么处理的。
复制代码

コードはGitHub

これは、コンテンツが含まれます。

生成された異常に対処する方法を開始しSpringBoot。


实现这个接口 ApplicationListener<ApplicationReadyEvent> ,

将 Bean 加入 ioc ,当程序启动发生异常你会感知到。比如启动失败发送邮件通知。

实现这个接口 ApplicationListener<ApplicationReadyEvent> ,
将 Bean 加入 ioc 容器中,当程序启动成功你会感知到。


基于实现 SpringBootExceptionReporter,对启动异常分析,
在我们自定义 starter 很有用
复制代码

SpringBoot例外は、実行中に発生しました。

@ExceptionHandler 和 @RestControllerAdvice 结合。处理标记的异常。

Tomcat 会根据 Request 判断是否有异常需要处理。
然后转发 DispatcherServlet url /error,这个可路径在 SpringBoot 可修改。

BasicErrorController 既是处理 /error 。

会讲一些源码,记录一下处理流程。
复制代码

ジェネリック型オブジェクト、親オブジェクトのクラスを分析

看源码的时候学到的,很强大的功能。ResolvableType
复制代码

例外処理のSpringBoot開始

SpringApplication.run(String... args) 中启动系统上下文,当发生异常的时候,
SpringApplication.handleRunFailure 处理启动异常逻辑。
1、会发送失败事件,可通过监听事件,处理逻辑。
2、SpringApplication.reportFailure 分析日常信息。
实现这个接口 SpringBootExceptionReporter 就可以注册异常了。
不过 SpringBoot FailureAnalyzers 给了默认实现。
我们可以基于 FailureAnalyzers 的逻辑进行扩展。
复制代码

イベントによって異常情報制御機構

ApplicationEventイベントによっておよびイベントをパブリッシュがうまく切り離すことができます。

私たちは、ビジネスイベント構造を介しても、カスタムのビジネスをすることができます。

故障の開始イベントをリッスン

@Component
public class StartFailedApplicationListener implements ApplicationListener<ApplicationFailedEvent> {

    @Autowired
    private EmailService emailService;

    @Override
    public void onApplicationEvent(ApplicationFailedEvent applicationFailedEvent) {
        // 发送邮件告诉启动失败了
        Throwable exception = applicationFailedEvent.getException();

//        31  红色   32  绿色 33  黄色
        StringJoiner stringJoiner = new StringJoiner("", "\031[32;4m", "\031[0m");
        String join = String.join("", "服务器 ip: 192.168.11.11 启动失败, 异常原因为:", exception.getMessage());
        String s = stringJoiner.add(join).toString();
        emailService.sendEmail(s);
    }
}
复制代码

インターフェイスのモニタの実装では、イベントの成功を開始します

@Component
public class StartSuccessApplicationListener implements ApplicationListener<ApplicationReadyEvent> {

    @Autowired
    private EmailService emailService;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        // 发送邮件告诉启动成功了
        //        31  红色   32  绿色 33  黄色
        StringJoiner stringJoiner = new StringJoiner("", "\033[32;4m", "\033[0m");
        String join = String.join("", "服务器 ip: 192.168.11.11 启动成功!");
        String s = stringJoiner.add(join).toString();
        emailService.sendEmail(s);
    }
}
复制代码

使用注釈モニター・イベント

@Component
public class AnnotationListener {

    @EventListener(value={ApplicationReadyEvent.class})
    public void annotationListener(){
        System.out.println(AnnotationListener.class.getName()+"启动成功了");
    }

}
复制代码

FailureAnalyzers例外分析

その例外処理を確認する連続AbstractFailureAnalyzerの最初のステップ、。

public class StartExceptionFailureAnalyzer extends AbstractFailureAnalyzer<RuntimeException> {
    @Override
    protected FailureAnalysis analyze(Throwable rootFailure, RuntimeException cause) {
        Throwable rootCause = cause.getCause();
        if (rootCause instanceof StartException) {
            return new FailureAnalysis("测试启动异常","",rootCause);
        }
        return null;
    }
}
复制代码

ハンドル例外ロジックを確認FailureAnalysisReporterを達成するための第二段階、。

public class MyFailureAnalysisReporter implements FailureAnalysisReporter {
    private EmailService emailService;
    public MyFailureAnalysisReporter(){
        emailService=new EmailService();
    }
    @Override
    public void report(FailureAnalysis analysis) {
        final Throwable cause = analysis.getCause();
        final String message = cause.getMessage();
        emailService.sendEmail(String.join("","异常原因:",message));
    }

}
复制代码

第三の部分は、上記2つのクラスspring.factoriesに添加しました

SpringFactoriesLoaderクラスはspring.factoriesをロードすることができます

org.springframework.boot.diagnostics.FailureAnalyzer=\
com.fly.exception.start.analyzer.StartExceptionFailureAnalyzer
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
com.fly.exception.start.analyzer.MyFailureAnalysisReporter
复制代码

サービスの実行時、例外処理

戻り値はとき、インタフェース安らかかなり役に立たない、ResponseEntity、簡単にセットアップリクエストヘッダとステータスコードをお勧めするように設定することができます。

@Component
@RestControllerAdvice
public class HandleActionException extends ResponseEntityExceptionHandler {
    public HandleActionException(){

    }

    @Override
    protected ResponseEntity handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
            request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
        }
        final ResponseEntity<RetUtil> retUtilResponseEntity = new ResponseEntity<>(RetUtil.build().code(5000), headers, status);
        return retUtilResponseEntity;
    }

    @ExceptionHandler(value = {RuntimeException.class})
    public ResponseEntity<RetUtil> handleRunTimeException(){
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(RetUtil.build().code(5000));
    }
}
复制代码

コードは、実質的に、処理ロジックは、以下

継承BasicErrorController処理/エラー・インタフェース

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class MyErrorController extends BasicErrorController {

    private ApplicationContext applicationContext;


    public MyErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties,
                                List<ErrorViewResolver> errorViewResolvers) {
        super( errorAttributes,  serverProperties.getError(), errorViewResolvers);
    }

    @Override
    @RequestMapping
    public ResponseEntity error(HttpServletRequest request) {
        HttpStatus status = getStatus(request);
        if (status == HttpStatus.NO_CONTENT) {
            return ResponseEntity.status(status).body(RetUtil.build().code(status.value()));
        }
        Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
        return new ResponseEntity<>(RetUtil.build().code(status.value()).data(body), status);
    }
}
复制代码

Response.sendError org.apache.coyote.Responseは、適切なステータスコードを設定し、errorState、errorState 1は問題があることを示しています。Tomcatの転送します/エラー

ErrorState 1かどうか、サービス処理として、フォワード/エラーを決定します

転送要求、転送/エラーは、フィルタを実行しません。

上述した画像フィルタ・チェーン・ロジックを実行します

次に、上述した画像、行うフィルタ責任パターンチェーン(フィルタの動作を行わない)のチェーン、および実行DispatcherServlet.doDispatch

ジェネリック型オブジェクト、親オブジェクトのクラスを分析

正確に得るためには一般的な修飾子

ワイルドカードを使用する場合は、修飾子を使用して、それを得ることができないではありません。

クラスのResolvableType説明

クラスのAPIパッケージの何かの等価、使用すると便利です。

ResolvableType.resolveGenerics 获取当前泛型。
ResolvableType.getInterfaces 获取父接口 Class 信息
ResolvableType.getSuperType 获取父类的 Class 信息
复制代码
   @Test
    public void run77() {
        final MyList55<String, Demo2> stringDemo2MyList55 = new MyList55<>();
        final ResolvableType resolvableType = ResolvableType.forInstance(stringDemo2MyList55);
        // null
        // null
        for (ResolvableType generic : resolvableType.getGenerics()) {
            System.out.println(generic.resolve());
        }
    }
复制代码

クラスの親インタフェースコードはジェネリック医薬品の使用方法を示します

public interface MyGenericInterface<T extends CharSequence, M extends Demo> {
   default void onApplicationEvent1(T event,M event3){
      System.out.println(event.charAt(1));
      System.out.println(event3.getName());
   }
}

public class MyList2 implements MyGenericInterface<String,Demo>{
}

public class MyList33 implements MyGenericInterface<String,Demo2> {
}

@Data
public class Demo {
    private String name;
}

public class Demo2 extends Demo {
}
复制代码

ジェネリック型のインターフェースを取得します

    @Test
    public void run22() {
        ResolvableType resolvableType = ResolvableType.forClass(MyList2.class);
        final ResolvableType[] interfaces = resolvableType.getInterfaces();
        final ResolvableType[] generics = interfaces[0].getGenerics();
        /**
         * class java.lang.String
         * class com.fly.exception.Demo
         */

        for (ResolvableType generic : generics) {
            System.out.println(generic.resolve());
        }
    }
    
    @Test
    public void run33() {
        ResolvableType resolvableType = ResolvableType.forClass(MyList33.class);
        final ResolvableType[] interfaces = resolvableType.getInterfaces();
        final ResolvableType[] generics = interfaces[0].getGenerics();
        /**
         * class java.lang.String
         * class com.fly.exception.Demo2
         */
        for (ResolvableType generic : generics) {
            System.out.println(generic.resolve());
        }
    }
复制代码

オブジェクトの親を取得するための汎用インターフェース

    @Test
    public void run44() {
        final MyList33 myList33 = new MyList33();
        final ResolvableType resolvableType = ResolvableType.forInstance(myList33);
        /**
         * class com.fly.exception.MyList33
         */
        System.out.println(resolvableType.resolve());
        final ResolvableType[] interfaces = resolvableType.getInterfaces();
        for (ResolvableType anInterface : interfaces) {
            final ResolvableType[] generics = anInterface.getGenerics();
            /**
             * class java.lang.String
             * class com.fly.exception.Demo2
             */
            for (ResolvableType generic : generics) {
                System.out.println(generic.resolve());
            }
        }
    }
复制代码

一般的なオブジェクト自体

@Test
public void run55() {
    MyList44<String, Demo> objectObjectMyList44 = new MyList44<>();
    final ResolvableType resolvableType = ResolvableType.forInstance(objectObjectMyList44);
    // class java.lang.String
    // class com.fly.exception.Demo
    for (ResolvableType generic : resolvableType.getGenerics()) {
        System.out.println(generic.resolve());
    }
}
复制代码

クラス自体は一般的な取得

 @Test
public void run66() {
    final ResolvableType resolvableType = ResolvableType.forClass(MyList44.class);
    // class java.lang.String
    // class com.fly.exception.Demo
    for (ResolvableType generic : resolvableType.getGenerics()) {
        System.out.println(generic.resolve());
    }
}
复制代码

おすすめ

転載: juejin.im/post/5dcfb17ef265da0bc33091f2