Spring boot 全局异常处理

Spring boot 全局异常处理

前言

@ControllerAdvice注解是Spring3.2中新增的注解,学名是Controller增强器,作用是给Controller控制器添加统一的操作或处理。

  1. 结合方法型注解@ExceptionHandler,用于捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的。
  2. 结合方法型注解@InitBinder,用于request中自定义参数解析方式进行注册,从而达到自定义指定格式参数的目的。
  3. 结合方法型注解@ModelAttribute,表示其注解的方法将会在目标Controller方法执行之前执行。

在启动应用之后,被@ExceptionHandler@InitBinder@ModelAttribute注解的方法都会作用在被@RequestMappping注解的方法上。@PostMapping其实也是被 @RequestMappping修饰的注解

这里主要介绍 全局异常的使用

涉及注解 描述
@RestControllerAdvice 该注解其实是 @ControllerAdvice + @ResponseBody的组合注解,其中@ControllerAdvice 包含 @Component,因此被该注解修饰的Java类也是一个被Spring 管理的Bean。
@ExceptionHandler 拦截异常并统一处理,主要在于声明一个或多个类型的异常,当符合条件的Controller抛出这些异常之后将会对这些异常进行捕获,然后按照其标注的方法的逻辑进行处理,从而改变返回的视图信息。
@ResponseStatus 设置响应状态码 详情参考:Spring @ResponseStatus

方便代码案例的演示 提前配置 好swagger。具体配置 参考: Spring boot 整合 Swagger使用swagger-bootstrap-ui

代码案例

作为全局异常处理,需要:

  • 自定义一些业务异常

  • 异常的返回码枚举

  • 通用的返回格式

  • 全局的异常捕捉Bean

(一)自定义一些业务异常

/**
 * @author lvzb
 * @date 2022/09/14  16:57
 **/
public class BusinessException extends RuntimeException {
    private static final long serialVersionUID = -8165592277866289665L;

    private final int errorCode;

    public BusinessException() {
        super("发生业务异常!");
        this.errorCode = 200;
    }

    public BusinessException(String message) {
        super(message);
        this.errorCode = 200;
    }

    public BusinessException(int errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }

    public BusinessException(String message, Throwable cause) {
        super(message, cause);
        this.errorCode = 200;
    }

    public BusinessException(Throwable cause) {
        super("发生业务异常!", cause);
        this.errorCode = 200;
    }

    public BusinessException(int errorCode, String message, Throwable cause) {
        super(message, cause);
        this.errorCode = errorCode;
    }

    public int getErrorCode() {
        return this.errorCode;
    }
}

(二)异常的返回枚举

public enum ErrorCode {

    SUCCESS(0, "成功"),
    ERROR(-1, "失败"),
    ERROR_500(500,"失败"),
    ERROR_200(200,"失败");

    @Getter
    private int code;
    @Getter
    private String message;

    ErrorCode(int code, String message) {
        this.code = code;
        this.message = message;
    }
}

(三)通用的返回格式

package com.zlv11.exception;

@Data
@Builder
public class CommonResponse<T> {

    private static final long serialVersionUID = -8713837118340960775L;

    private T data;
    private int code;
    private String message;

    public static CommonResponse success() {
        return CommonResponse.builder()
                .code(ErrorCode.SUCCESS.getCode())
                .message(ErrorCode.SUCCESS.getMessage())
                .build();
    }

    public static <T> CommonResponse success(T data) {
        return CommonResponse.builder()
                .code(ErrorCode.SUCCESS.getCode())
                .data(data)
                .message(ErrorCode.SUCCESS.getMessage())
                .build();
    }

    public static CommonResponse error(ErrorCode errorCode) {
        return CommonResponse.builder()
                .code(errorCode.getCode())
                .message(errorCode.getMessage())
                .build();
    }

    public static CommonResponse error(ErrorCode errorCode, String message) {
        return CommonResponse.builder()
                .code(errorCode.getCode())
                .message(StringUtils.isEmpty(message) ? errorCode.getMessage() : message)
                .build();
    }

}

(四)全局的异常捕捉Bean

/**
 * 全局异常处理
 *
 * @author zlv11
 * @date 2022/08/05  19:17
 */
@Slf4j
@RestControllerAdvice
@Order(value = Ordered.HIGHEST_PRECEDENCE)
public class GlobalExceptionHandler {

    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public CommonResponse<String> handleException(Exception e) {
        log.error("系统内部异常,异常信息:{}", e);
        return CommonResponse.error(ErrorCode.ERROR_500, "系统内部异常");
    }

    @ExceptionHandler(value = BusinessException.class)
    @ResponseStatus(code = HttpStatus.OK,reason = "业务异常")
    public CommonResponse<String> BusinessExceptionHandler(BusinessException e) {
        log.error("业务异常,异常信息:{}", e);
        return CommonResponse.error(ErrorCode.ERROR_200, e.getMessage());
    }
     /**
     * 统一处理请求参数校验(实体对象传参)
     *
     * @param e BindException
     * @return CommonResponse
     */
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public CommonResponse validExceptionHandler(BindException e) {
        StringBuilder message = new StringBuilder();
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        for (FieldError error : fieldErrors) {
            message.append("[").append(error.getField()).append("]").append(error.getDefaultMessage()).append(StringPool.COMMA);
        }
        message = new StringBuilder(message.substring(0, message.length() - 1));
        return CommonResponse.error(ErrorCode.ERROR, message.toString());
    }

    /**
     * 统一处理请求参数校验(实体对象传参)
     *
     * @param e MethodArgumentNotValidException
     * @return CommonResponse
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public CommonResponse validExceptionHandler(MethodArgumentNotValidException e) {
        StringBuilder message = new StringBuilder();
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        for (FieldError error : fieldErrors) {
            message.append("[").append(error.getField()).append("]").append(error.getDefaultMessage()).append(StringPool.COMMA);
        }
        message = new StringBuilder(message.substring(0, message.length() - 1));
        log.error("请求入参异常,异常信息:{}", message);
        return CommonResponse.error(ErrorCode.ERROR, message.toString());
    }

    /**
     * 统一处理请求参数校验(普通传参)
     *
     * @param e ConstraintViolationException
     * @return CommonResponse
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public CommonResponse handleConstraintViolationException(ConstraintViolationException e) {
        StringBuilder message = new StringBuilder();
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        for (ConstraintViolation<?> violation : violations) {
            Path path = violation.getPropertyPath();
            String[] pathArr = StringUtils.splitByWholeSeparatorPreserveAllTokens(path.toString(), StringPool.DOT);
            message.append(pathArr[1]).append(violation.getMessage()).append(StringPool.COMMA);
        }
        message = new StringBuilder(message.substring(0, message.length() - 1));
        return CommonResponse.error(ErrorCode.ERROR, message.toString());
    }
}

(五)测试的controller层

/**
 * @author lvzb
 * @date 2022/11/15  17:37
 **/
@Slf4j
@RestController
@Api(tags = "控制层")
@RequestMapping("/swagger")
public class SwaggerTestController {

    @PostMapping("/test")
    public EchoResponse echo(@RequestBody EchoRequest request) {
        return EchoResponse.builder().resp1("Hello world!").build();
    }

    @PostMapping("/runtimeException")
    public CommonResponse<String> runtimeException(@RequestBody EchoRequest request) {
        throw new RuntimeException("运行时异常 runtimeException");
    }

    @PostMapping("/businessException")
    public CommonResponse<String> businessException(@RequestBody EchoRequest request) {
        throw new BusinessException("业务异常 businessException");
    }
}

参考资料

  • spring的@ControllerAdvice注解
  • Spring @ResponseStatus
  • @ControllerAdvice 的介绍及三种用法
  • SpringBoot 全局异常处理进阶:使用 @ControllerAdvice 对不同的 Controller 分别捕获异常并处理

你可能感兴趣的:(java,spring,boot,java,spring)