Spring Boot handles global exceptions uniformly

Introduction to @ControllerAdvice

The @ControllerAdvice annotation is a new annotation in Spring 3.2. The scientific name is Controller Enhancer, which is used to add unified operations or processing to the Controller controller.

Here, ControllerAdvice can also be understood in this way. Its abstraction level should be used to surround the Controller, and the specific business weaving method is realized by combining other annotations.

usage

1. Combined with the method annotation @ExceptionHandler, it is used to capture the specified type of exception thrown in the Controller, so as to achieve the purpose of different types of exception processing.

2. Combined with the method annotation @InitBinder, it is used to register the custom parameter parsing method in the request, so as to achieve the purpose of customizing the specified format parameters.

3. Combined with the method annotation @ModelAttribute, it means that the annotated method will be executed before the target Controller method is executed.

As can be seen from the above explanation, the usage of @ControllerAdvice is basically to declare it on a bean, and then use other annotations on the method of the bean to specify different weaving logic. However, @ControllerAdvice here does not use AOP to weave business logic, but Spring has built-in support for the weaving of its various logics.

@Component @ExceptionHandler for classes that declare @ExceptionHandler , @InitBinder or @ModelAttribute methods, @InitBinder is shared among multiple @Controller classes.

Classes annotated with @ControllerAdvice can be explicitly declared as Spring beans or automatically detected by classpath scanning. All such beans are Ordered according to Ordered semantics or @Order/@Priority declarations, Ordered semantics take precedence over @Order/@Priority declarations. The @ControllerAdvice beans are then applied in that order at runtime. Note, however, that a bean implementing PriorityOrdered @ControllerAdvice has no higher PriorityOrdered than a bean implementing Ordered @ControllerAdvice. Also, Ordered does not apply to a scoped @ControllerAdvice eg if such a bean has been configured as a request scoped or session scoped bean. For handling exceptions, @ExceptionHandler will be selected in the first notification with a matching exception handler method. For model attribute and data binding initialization, @ModelAttribute and @InitBinder methods will follow the @ControllerAdvice order.

Note: For @ExceptionHandler methods, in the handler method of a particular advice bean, root exception matching will take precedence over matching only the cause of the current exception. However, a cause match on a higher priority suggestion is still preferred over any match on a lower priority suggestion bean, whether at the root or cause level. Therefore, declare your main root exception mapping on the priority advice bean in the appropriate order.

By default, @ControllerAdvice methods in ControllerAdvice apply globally to all controllers. Use selectors such as annotations, basePackageClasses, and basePackages (or their alias value) to define a narrower subset of target controllers. If multiple selectors are declared, boolean OR logic is applied, which means that the selected controller should match at least one selector. Note that selector checking is performed at runtime, so adding many selectors can negatively impact performance and increase complexity.

@ExceptionHandler intercepts exceptions and handles them uniformly

Combined with the @ExceptionHandler annotation, when the exception is thrown to the controller layer, the exception can be handled uniformly, specifying the returned json format or jumping to the specified error page, etc.

The function of @ExceptionHandler is mainly to declare one or more types of exceptions. When the qualified Controller throws these exceptions, these exceptions will be caught, and then processed according to the logic of the methods marked by them, thereby changing the returned view information .

Annotation for handling exceptions in a specific handler class and/or handler method.

Handler methods annotated with this annotation allow to have very flexible signatures. They may have parameters of the following types, in any order:

Exception parameter: declared as a general exception or a more specific exception. This can also be used as a mapping hint if the annotation itself does not narrow the exception type by its value()

Code

/**
 * 自定义一个异常类,用于处理我们发生的业务异常

 */
public class BizException extends RuntimeException {
 
    private static final long serialVersionUID = 1L;
 
    /**
     * 错误码
     */
    protected String errorCode;
    /**
     * 错误信息
     */
    protected String errorMsg;
 
    public BizException() {
        super();
    }
 
    public BizException(FrontResult errorInfoInterface) {
        super(errorInfoInterface.getCode());
        this.errorCode = errorInfoInterface.getMessage();
        this.errorMsg = errorInfoInterface.getMessage();
    }
 
    public BizException(FrontResult errorInfoInterface, Throwable cause) {
        super(errorInfoInterface.getCode(), cause);
        this.errorCode = errorInfoInterface.getCode();
        this.errorMsg = errorInfoInterface.getMessage();
    }
 
    public BizException(String errorMsg) {
        super(errorMsg);
        this.errorMsg = errorMsg;
    }
 
    public BizException(String errorCode, String errorMsg) {
        super(errorCode);
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }
 
    public BizException(String errorCode, String errorMsg, Throwable cause) {
        super(errorCode, cause);
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }
 
 
    public String getErrorCode() {
        return errorCode;
    }
 
    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }
 
    public String getErrorMsg() {
        return errorMsg;
    }
 
    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }
 
    public String getMessage() {
        return errorMsg;
    }
 
    @Override
    public Throwable fillInStackTrace() {
        return this;
    }
 
}

Unified exception handling

 

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
 
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.sql.SQLException;
 
/**
 * 统一异常处理

 */
@ControllerAdvice//使用该注解表示开启了全局异常的捕获
public class GlobalExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
 
    /**
     * 处理自定义的业务异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value = BizException.class)
    @ResponseBody
    public  FrontResult bizExceptionHandler(HttpServletRequest req, BizException e){
        logger.error("URL : " + req.getRequestURL().toString());
        logger.error("HTTP_METHOD : " + req.getMethod());
        logger.error("发生业务异常!原因是:{}",e.getErrorMsg());
        return FrontResult.getExceptionResult(e.getErrorCode(),e.getErrorMsg());
    }
 
    /**
     * 处理空指针的异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value =NullPointerException.class)
    @ResponseBody
    public FrontResult exceptionHandler(HttpServletRequest req, NullPointerException e)  {
        logger.error("URL : " + req.getRequestURL().toString());
        logger.error("HTTP_METHOD : " + req.getMethod());
        logger.error("发生空指针异常!原因是:",e);
        return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg());
    }
 
    /**
     * 处理索引越界异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value =IndexOutOfBoundsException.class)
    @ResponseBody
    public FrontResult exceptionHandler(HttpServletRequest req, IndexOutOfBoundsException e){
        logger.error("URL : " + req.getRequestURL().toString());
        logger.error("HTTP_METHOD : " + req.getMethod());
        logger.error("索引越界异常!原因是:",e);
        return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg());
    }
 
    /**
     * 处理类未找到异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value =ClassNotFoundException.class)
    @ResponseBody
    public FrontResult exceptionHandler(HttpServletRequest req, ClassNotFoundException e)  {
        logger.error("URL : " + req.getRequestURL().toString());
        logger.error("HTTP_METHOD : " + req.getMethod());
        logger.error("发生类未找到异常!原因是:",e);
        return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg());
    }
 
 
    /**
     * 处理SQL异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value = SQLException.class)
    @ResponseBody
    public FrontResult exceptionHandler(HttpServletRequest req, SQLException e)  {
        logger.error("URL : " + req.getRequestURL().toString());
        logger.error("HTTP_METHOD : " + req.getMethod());
        logger.error("发生SQL异常!原因是:",e);
        return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg());
    }
 
    /**
     * 处理IO异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value = IOException.class)
    @ResponseBody
    public FrontResult exceptionHandler(HttpServletRequest req, IOException e)  {
        logger.error("URL : " + req.getRequestURL().toString());
        logger.error("HTTP_METHOD : " + req.getMethod());
        logger.error("发生IO异常!原因是:",e);
        return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg());
    }
 
 
    /**
     * 处理其他异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value =Exception.class)
    @ResponseBody
    public FrontResult exceptionHandler(HttpServletRequest req, Exception e){
        logger.error("URL : " + req.getRequestURL().toString());
        logger.error("HTTP_METHOD : " + req.getMethod());
        logger.error("未知异常!原因是:",e);
        return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg());
    }
 
 
 
}

Front-end return value class

 
 

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
 
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FrontResult {
    /**
     * 结果状态码
     */
    private String code;
    /**
     * 响应结果描述
     */
    private String message;
    /**
     * 返回数据
     */
    private Object data;
 
    /**
     * 静态方法,返回前端实体结果
     *
     * @param code    状态码
     * @param message 消息
     * @param data    数据
     * @return 前端实体结果
     */
    public static FrontResult build(String code, String message, Object data) {
        return new FrontResult(code, message, data);
    }
 
    /**
     * 返回成功的结果实体
     *
     * @param message 消息
     * @param data    数据
     * @return 实体
     */
    public static FrontResult getSuccessResult(String message, Object data) {
        FrontResult result = new FrontResult();
        result.code = ResultCodeEnum.SUCCESS.getCode();
        result.message = message;
        result.data = data;
        return result;
    }
 
    /**
     * 返回无需data的成功结果实体
     *
     * @param message 消息内容
     * @return 返回结果
     */
    public static FrontResult getSuccessResultOnlyMessage(String message) {
        FrontResult result = new FrontResult();
        result.code = ResultCodeEnum.SUCCESS.getCode();
        result.message = message;
        result.data = null;
        return result;
    }
 
    /**
     * 获取一个异常结果
     *
     * @param code 错误码
     * @param message 自定义异常信息
     * @return FrontResult
     */
    public static FrontResult getExceptionResult(String code, String message) {
        FrontResult result = new FrontResult();
        result.code = code.isEmpty() ? ResultCodeEnum.CODE_EXCEPTION.getCode() : code;
        result.message = message.isEmpty() ? ResultCodeEnum.CODE_EXCEPTION.getMsg() : message;
        return result;
    }
}
import lombok.AllArgsConstructor;
 
@AllArgsConstructor
public enum ResultCodeEnum {
    // 请求成功
    SUCCESS("0000"),
    // 请求失败
    FAIL("1111"),
    // EXCEL 导入失败
    EXCEL_FAIL("1000"),
    // userID 为空
    ID_NULL("1001"),
    // 前端传的实体为空
    MODEL_NULL("1002"),
    // 更新失败
    UPDATE_FAIL("1011"),
    // 参数为空
    PARAM_ERROR("400"),
    // 代码内部异常
    CODE_EXCEPTION("500", "代码内部异常");
 
    /**
     * 状态码
     */
    private String code;
 
    public String getCode() {
        return code;
    }
 
    ResultCodeEnum(String code) {
        this.code = code;
    }
 
    private String msg;
 
    public String getMsg() {
        return msg;
    }
 
}
 
 
public enum ResutlMsgEnum {
 
    //查询成功
    FIND_SUCCESS("查询成功!"),
    //查询失败
    FIND_FAIL("查询失败!"),
 
    //更新成功
    UPDATE_SUCCESS("更新成功"),
    //更新失败
    UPDATE_FAIL("更新成功"),
   
    SEND_SUCCESS("发送成功"),
 
    SEND_FAIL("发送失败");
 
 
    private String msg;
 
    ResutlMsgEnum(String msg) {
        this.msg = msg;
    }
 
    public String getMsg() {
        return msg;
    }
}
/**
 * 测试用例

 */
@Api(tags = {"测试controller"})
@RequestMapping(value = "/testController")
@RestController
public class TestController {
 
   
    @ApiOperation(value = "测试null")
    @GetMapping(value = "getNull")
    public FrontResult getNull() {
        int length = 0;
 
            String name=null;
            length = name.length();
 
 
        return FrontResult.build(ResultCodeEnum.SUCCESS.getCode(),
                ResutlMsgEnum.EXECUTE_SUCCESS.getMsg(), length);
    }
}

Guess you like

Origin blog.csdn.net/liuerchong/article/details/123712804