Home  >  Article  >  Java  >  How to configure SpringBoot global exception handler to catch exceptions

How to configure SpringBoot global exception handler to catch exceptions

王林
王林forward
2023-05-14 21:52:182002browse

1. Preface

In any system, we will not stupidly catch and handle exceptions in every place. Generally, we will handle exceptions in one place for the entire system, and handle global exceptions in spring boot. Very simple;

Separation of front and back ends, back-end API, generally for exception handling, there are only two things to do,

1. Record logs and corresponding notification processing, this It is internal

2. It is to return the result to the API caller, it is external

For the API caller, he only It needs a return result (including error code and prompt information), and he doesn't care about the rest.

For the backend, it only needs to record logs, notify or publish corresponding messages to other queues for processing related matters;

So: I have seen many people encapsulating many custom exception classes. In fact, it is completely unnecessary. You only need one exception handler to handle all exceptions, and then encapsulate an error identification code and prompt message. Enumeration is used to return to the API caller; then the back-end processing can be processed directly in one exception handling method. There is no need to encapsulate N multiple custom exceptions, which makes no sense;

Ideological understanding of exceptions

We should realize that all abnormalities are abnormal manifestations of the system, are defects, and are BUGs, although some exceptions are We actively throw;

What we have to do is to improve the system availability as much as possible and avoid the occurrence of any exceptions to the greatest extent, rather than relying on perfect exception handling to improve the system;

Exception Handling is an emergency measure taken when exceptions inevitably occur. The main purpose is to increase friendliness to the outside world and provide remedial clues to the inside;

Don't think that perfect exception handling is the core of the system, it is not. Don't expect exception handling to be perfect, don't expect exception handling to wipe out system defects;

If the system has too many exceptions, then what you have to do is not to improve the exception handling mechanism, but to reflect on the system architecture: Whether the design is reasonable and whether the system logic design is reasonable;

2. Method 1 of global exception handling (@ControllerAdvice and @ExceptionHandler)

============ =====================================

Under development, we will have The following scenario: There are some business exceptions in a certain interface. For example, the parameters entered by the user fail to be verified, the username and password do not exist, etc. When these business exceptions are triggered, we need to throw these custom business exceptions and handle them. Generally, we need to return the status code and exception description of these exception information to the caller in a friendly manner, and the caller uses the status code and other information to determine the specific circumstances of the exception.

In the past, we might need to handle it through try/catch at the controller layer. First catch custom exceptions, then catch other exceptions. For different exceptions, we need to encapsulate the object to be returned while catching. However, the downside of this is that the code becomes verbose. Each interface requires try/catch processing, and once it needs to be adjusted, all interfaces need to be modified, which is very detrimental to code maintenance, as shown in the following code section

@RequestMapping (value = "/test")
public ResponseEntity test() {
    ResponseEntity re = new ResponseEntity();
    // 业务处理
    // ...
    try {
        // 业务
    } catch (BusinessException e) {
        logger.info("业务发生异常,code:" + e.getCode() + "msg:" + e.getMsg());
        re.setCode(e.getCode());
        re.setMsg(e.getMsg());
        return re;
    } catch (Exception e) {
        logger.error("服务错误:", e);
        re.setCode("xxxxx");
        re.setMsg("服务错误");
        return re;
    }
    return re;
}

So, is there any way to do this? How to handle these exception messages easily? The answer is yes. In Spring 3.2, the @ControllerAdvice annotation is added, which can be used to define @ExceptionHandler, @InitBinder, and @ModelAttribute, and is applied to all @RequestMapping. To put it simply, you can configure a global exception handling class through the @ControllerAdvice annotation to uniformly handle exceptions in the controller layer. At the same time, there is no need to write try/catch in the controller, which makes the code clean and easy to maintain.

Usage method

Define custom exceptions

I won’t go into details about the relevant knowledge points about custom exceptions here. If you don’t know, search it yourself. Paste a simple custom business exception class here.

/**
 * 自定义业务异常类
 *
 * @author Yuzhe Ma
 * @date 2018/11/28
 */
@Data
public class BusinessException extends RuntimeException {
    private String code;
    private String msg;
 
    public BusinessException(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}

Note: @Data is a Lombok plug-in. Automatically generate set/get methods. The specific usage method will not be introduced here.

@ControllerAdvice @ExceptionHand` Configure the global exception handling class

/**
 * 全局异常处理器
 *
 * @author Yuzhe Ma
 * @date 2018/11/12
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
 
    /**
     * 处理 Exception 异常
     *
     * @param httpServletRequest httpServletRequest
     * @param e                  异常
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public ResponseEntity exceptionHandler(HttpServletRequest httpServletRequest, Exception e) {
        logger.error("服务错误:", e);
        return new ResponseEntity("xxx", "服务出错");
    }
 
    /**
     * 处理 BusinessException 异常
     *
     * @param httpServletRequest httpServletRequest
     * @param e                  异常
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = BusinessException.class)
    public ResponseEntity businessExceptionHandler(HttpServletRequest httpServletRequest, BusinessException e) {
        logger.info("业务异常。code:" + e.getCode() + "msg:" + e.getMsg());
        return new ResponseEntity(e.getCode(), e.getMsg());
    }
}

@ControllerAdvice

Define this class as global exception handling kind.

@ExceptionHandler

Define this method as an exception handling method. The value of value is the class file of the exception class that needs to be handled. In the example, the method is passed in two parameters. One is the corresponding Exception exception class, and the other is the HttpServletRequest class. Of course, in addition to these two parameters, some other parameters are also supported.

In this way, different exceptions can be handled uniformly. Usually, in order to avoid using any try/catch in the controller, Exception can also be processed uniformly in GlobalExceptionHandler. In this way, other exceptions not configured with @ExceptionHandler will be handled uniformly.

Just throw an exception when encountering an exception

In business, when encountering a business exception, just use throw to throw the corresponding business exception. For example,

throw new BusinessException("3000", "账户密码错误");

is written in Controller

Controller 中,不需要再写 try/catch,除非特殊用途。

@RequestMapping(value = "/test")
public ResponseEntity test() {
    ResponseEntity re = new ResponseEntity();
    // 业务处理
    // ...
    return re;
}

结果展示

异常抛出后,返回如下结果。

{
    "code": "3000",
    "msg": "账户密码错误",
    "data": null
}

注意 不一定必须在 controller 层本身抛出异常才能被 GlobalExceptionHandler 处理,只要异常最后是从 contoller 层抛出去的就可以被全局异常处理器处理。异步方法中的异常不会被全局异常处理。抛出的异常如果被代码内的 try/catch 捕获了,就不会被 GlobalExceptionHandler 处理了。总结

本文介绍了在 SpringBoot 中,通过配置全局异常处理器统一处理 Controller 层引发的异常。

优点

减少代码冗余,代码便于维护

缺点

只能处理 controller 层抛出的异常,对例如 Interceptor(拦截器)层的异常、定时任务中的异常、异步方法中的异常,不会进行处理。

3.全局异常并处理的方法二 (AOP)

虽然@ControllerAdvice注解通常和@ExceptionHandler注解用于全局异常的处理。

但是这种方式有个缺点就是,只是对控制层进行了异常拦截,比如像工具类中或者其他类中的异常,并不会拦截。

由于业务执行时不能保证程序不出错,所以写代码必须添加try-catch,但是如果频繁的添加try-catch则必然导致代码结构混乱.所以需要进行优化.

原则:如果出现了问题一般将检查异常,转化为运行时异常.

核心原理: 代理动态思想------->AOP操作

采用自定义AOP的方式可以实现拦截。

有几个关键点

  1. 定义切入点为最大项目包

  2. 采用AOP的@AfterThrowing注解获取到全局异常捕获一个例子package com.example.promethuesdemo.exception; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; /** * @author chenzhen * Created by chenzhen on 2020/7/20.

*/
    @Aspect
    @Slf4j
    @Component
    public class GlobalExceptionAspect {
        @Pointcut("execution(* com.example..*.*(..))")
        public void pointcut(){
 
        }
 
        @AfterThrowing(pointcut = "pointcut()",throwing = "e")
        public void afterThrowing(JoinPoint joinPoint,Throwable e){
            log.error("全局捕获到异常了..............");
            //纪录错误信息
            log.error("系统错误:{}", e.getMessage());
            // todo 想要执行的操作
        }
 
    }

aop中相关概念

Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。* Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它

joint point。* Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。* Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。* Target(目标对象):织入 Advice 的目标对象.。 Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程

Advice(增强)的类型

before advice, 在 join point 前被执行的 advice. 虽然 before advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常(即我们在 before advice 代码中,不能人为地决定是否继续执行 join point 中的代码)* after return advice, 在一个 join point 正常返回后执行的 advice* after throwing advice, 当一个 join point 抛出异常后执行的 advice* after(final) advice, 无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice.* around advice, 在 join point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice.* introduction,introduction可以为原有的对象增加新的属性和方法。

注意

spring AOP中的AfterThrowing增强处理可以对目标方法的异常进行处理,但这种处理与直接使用catch捕捉处理异常的方式不同,catch捕捉意味着能完全处理异常,即只要catch块本身不抛出新的异常,则被处理的异常不会往上级调用者进一步传播下去;但是如果使用了AfterThrowing增强处理用于对异常进行处理,处理后异常仍然会往上一级调用者传播,如果是在main中调用的目标方法,那么异常会直接传到JVM,如下截图所示:

How to configure SpringBoot global exception handler to catch exceptions

SpringBoot 之配置全局异常处理器捕获异常

Also note that if an exception occurs in the target method and is caught and processed by catch and catch does not throw a new exception, then the AfterThrowing enhancement processing for the target method will not be executed.

The above is the detailed content of How to configure SpringBoot global exception handler to catch exceptions. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete