SpringBoot's configuration of global exception handlers to catch exceptions

SpringBoot's configuration of global exception handlers to catch exceptions

1 Introduction

In any system, we will not silly catch and handle exceptions in every place. In the whole system, we will generally handle exceptions in one place. Spring boot global exception handling is very simple;

Before the introduction, let me talk a little bit off topic. Our current development system is completely separated from the front and the back. The backend only provides RESTfull API. It is forbidden to involve any interface. Thymeleaf and JSP backend templates are absolutely forbidden. Throw the trash can, don’t waste great youth to research, that is depravity; the front-end is responsible for interface-related, commonly used Vue; if the company has not separated the front-end and the back-end, and write together in thymeleaf, then you should plan to change jobs early. , They can't support you, let alone support your family;

Front-end and back-end separation, back-end API, generally for exception handling, there are two things to do,

1. It records the log and corresponding notification processing, which is internal,

2. It is to give the return result to the API caller, which is external;

For the API caller, he only needs one return result (including error code and prompt information), and he doesn’t care about the others

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

So: I have seen many people encapsulate a lot of custom exception classes. In fact, it is completely unnecessary. Only one exception handler is needed to handle all exceptions, and then an enumeration of error identification codes and prompt messages is encapsulated for 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 has no meaning;

Thinking about abnormality

We should realize that all exceptions are abnormal behaviors for the system, are defects, and all belong to BUG, ​​although some exceptions are thrown by us on our own initiative;

What we have to do is to maximize the availability of the system and avoid any exceptions to the greatest extent possible, instead of looking to perfect exception handling to improve the system;

Exception handling is an emergency measure taken when an abnormality cannot be avoided. The main purpose is to increase friendliness to the outside and to provide remedial clues internally;

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

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

2. The first method of global exception handling (@ControllerAdvice and @ExceptionHandler)

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

During development, we will have the following scenario: In a certain interface, there are some business exceptions. For example, the verification of the parameters entered by the user fails, and the user name and password do not exist. 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 the exception information to the caller friendly, and the caller uses the status code and other information to determine the specific situation of the exception.

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

@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 easily handle these abnormal information? The answer is yes. In Spring 3.2, the @ControllerAdvice annotation is added, which can be used to define @ExceptionHandler, @InitBinder, @ModelAttribute, and apply to all @RequestMapping. Simply put, you can configure a global exception handling class through the @ControllerAdvice annotation to handle exceptions in the controller layer uniformly. At the same time, there is no need to write try/catch in the controller, which makes the code clean and easy to maintain.

Instructions

Define custom exception

The relevant knowledge points about custom exceptions are not explained in detail here, if you don’t know, please search for 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 the Lombok plugin. Automatically generate set/get methods. The specific usage method will not be introduced here.

@ControllerAdvice + @ExceptionHand` Configure 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 a global exception handling class.

@ExceptionHandler

Define this method as an exception handling method. The value of value is the class file of the exception class to be handled. In the example, the method passes 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. See the document https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ExceptionHandler.html for details

In this way, different exceptions can be handled uniformly. Usually, in order to make the controller no longer use any try/catch, you can also do a unified treatment of Exception in GlobalExceptionHandler. In this way, other exceptions that are not configured with @ExceptionHandler will be handled uniformly.

Throw an exception when you encounter an exception

In business, where business exceptions are encountered, throw the corresponding business exceptions directly using throw. E.g

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

How to write in Controller

In Controller, there is no need to write try/catch, except for special purposes.

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

Result display

After the exception is thrown, the following result is returned.

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

note

  1. It is not necessary to throw an exception at the controller layer itself to be handled by the GlobalExceptionHandler, as long as the exception is finally thrown from the contoller layer, it can be handled by the global exception handler.
  2. Exceptions in asynchronous methods will not be handled by global exceptions.
  3. If the thrown exception is caught by the try/catch in the code, it will not be handled by the GlobalExceptionHandler.

to sum up

This article introduces how to handle exceptions raised by the Controller layer by configuring the global exception handler in SpringBoot.

advantage

Reduce code redundancy, code is easy to maintain

Disadvantage

It can only handle exceptions thrown by the controller layer. For example, exceptions in the Interceptor (interceptor) layer, exceptions in timing tasks, and exceptions in asynchronous methods will not be processed.

The above is to use @ControllerAdvice + @ExceptionHand to implement the method of catching and handling global exceptions at the controller layer in SpringBoot.

3. The second method of global exception handling (AOP)

Although @ControllerAdvice annotation is usually used with @ExceptionHandler annotation for global exception handling.

But this method has the disadvantage that it only intercepts exceptions in the control layer, such as exceptions in the tool class or other classes, and will not intercept it.

Since the program cannot be guaranteed to be error-free during business execution, try-catch must be added to write code, but if try-catch is frequently added, the code structure will inevitably lead to confusion. So optimization is needed.

Principle: If there is a problem, the exception will be checked and converted into a runtime exception.

Core principle: Agent dynamic thinking------->AOP operation

Interception can be achieved by using custom AOP.

There are a few key points

  1. Define the entry point as the largest project package
  2. Use AOP's @AfterThrowing annotation to get an example of global exception capture 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 想要执行的操作
        }

    }

Related concepts in aop

  • Aspect (Aspect): Aspect declaration is similar to class declaration in Java. Aspect will contain some Pointcuts and corresponding Advices. * Joint point (joint point): indicates a clearly defined point in the program, typically including method calls, access to class members and execution of exception handler blocks, etc. It can also nest other
    joint points. * Pointcut (pointcut): Represents a set of joint points, these joint points are either combined through logical relationships, or centralized through wildcards, regular expressions, etc., which define the place where the corresponding advice will occur. * Advice (enhanced): Advice defines the specific operations to be performed at the program points defined in Pointcut. It distinguishes whether it is before, after, or instead of executing code through before, after and around. * Target (target object): the target object woven into Advice.. Weaving: the process of connecting Aspect and other objects and creating an Adviced object

Advice (enhanced) type

  • Before advice, the advice that is executed before the join point. Although before advice is executed before the join point, it cannot prevent the execution of the join point, unless an exception occurs (that is, we
    cannot artificially execute the before advice code. Decide whether to continue executing the code in the join point) * after return advice, advice that is executed after a join point returns normally* after throwing advice, advice that is executed after a join point throws an exception* after(final) advice, no matter one Whether the join point exits normally or an exception occurs, will be the advice that will be executed. * Around advice, the advice that is executed before the join point and after the joint point exits. This is the most commonly used advice. * Introduction, the introduction can be the original The object adds new properties and methods.

note

AfterThrowing enhanced processing in spring AOP can handle the exceptions of the target method, but this processing is different from the way of directly using catch to handle exceptions. Catch catching means that exceptions can be completely handled, that is, as long as the catch block itself does not throw new Exception, the processed exception will not be further propagated to the higher-level caller; but if the AfterThrowing enhanced processing is used to handle the exception, the exception will still be propagated to the upper-level caller after processing, if it is called in main Target method, then the exception will be directly transmitted to the JVM, as shown in the screenshot below:

SpringBoot's configuration of global exception handlers to catch exceptions

In addition, it should be noted that if an exception occurs in the target method and is caught by the catch and the catch does not throw a new exception, the AfterThrowing enhancement processing for the target method will not be executed.

Guess you like

Origin blog.csdn.net/doubututou/article/details/112858562