前言

在业务开发中,异常捕捉是必不可少的一步,最简单的方法就是使用try/catch。

但是,当程序规模逐渐扩大,每个都使用catch时,就会使得代码非常臃肿,耦合度也可能大大增加。所以我们需要利用Spring的AOP特性解决这一问题。

代码结构

image-1660312833883

涉及依赖

<!--web-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!--参数验证工具-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

<!--Lombok-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

代码

NykbExceptionEnum

/**
 * 异常类型枚举
 */
public enum NykbExceptionEnum {

    /**
     * 操作成功
     */
    SUCCESS(200, "操作成功"),

    /**
     * 请求格式错误
     */
    BAD_REQUEST(400, "请求格式错误"),

    /**
     * 未登录或者身份已过期
     */
    NOT_AUTH(401, "未登录或者凭证已过期"),

    /**
     * 无权操作
     */
    REFUSER(403, "无权操作"),

    /**
     * 请求的资源找不到
     */
    NOT_FOUND(404, "请求的资源找不到"),

    /**
     * 服务器内部错误
     */
    SERVER_ERROR(500, "服务器错误");

    private int code;

    private String message;

    NykbExceptionEnum() {
    }

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

    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

NykbException

/**
 * 自定义异常
 */
@RestControllerAdvice
public class NykbException extends RuntimeException {
    private int code;

    public NykbException() {
    }

    public NykbException(String message) {
        super(message);
        this.code = NykbExceptionEnum.SERVER_ERROR.getCode();
    }

    public NykbException(NykbExceptionEnum exceptionEnum) {
        super(exceptionEnum.getMessage());
        this.code = exceptionEnum.getCode();
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }
}

NykbExceptionHandler

/**
 * 异常处理中心
 */
@Slf4j
@RestController
@RestControllerAdvice
public class NykbExceptionHandler {

    /**
     * 自定义异常
     */
    @ExceptionHandler(value = NykbException.class)
    @ResponseStatus(HttpStatus.OK)
    public <T> CommonResult<T> handler(NykbException e) {
        log.error(e.getMessage());
        return CommonResult.failed(e);
    }

    /**
     * 数据校验异常
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.OK)
    public <T> CommonResult<T> handler(ConstraintViolationException e) {
        log.error(e.getMessage());
        return CommonResult.failed(e.getMessage());
    }

    /**
     * 缺少参数异常
     */
    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    @ResponseStatus(HttpStatus.OK)
    public <T> CommonResult<T> handler(MissingServletRequestParameterException e) {
        log.error(e.getMessage());
        return CommonResult.failed(e.getMessage());
    }

    /**
     * 其他异常
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(HttpStatus.OK)
    public <T> CommonResult<T> handler(Exception e) {
        log.error(ErrorUtil.getErrorCause(e));
        return CommonResult.failed(ErrorUtil.getErrorCause(e));
    }

}

CommonResult

/**
 * 返回数据封装
 */
@Data
public class CommonResult<T> {
    private int code;

    private String message;

    private T data;

    public CommonResult() {
    }

    public CommonResult(int code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public static <T> CommonResult<T> success() {
        return new CommonResult<>(NykbExceptionEnum.SUCCESS.getCode(),
                NykbExceptionEnum.SUCCESS.getMessage(), null);
    }

    public static <T> CommonResult<T> success(String message) {
        return new CommonResult<>(NykbExceptionEnum.SUCCESS.getCode(),
                message, null);
    }

    public static <T> CommonResult<T> success(T data) {
        return new CommonResult<>(NykbExceptionEnum.SUCCESS.getCode(),
                NykbExceptionEnum.SUCCESS.getMessage(), data);
    }

    public static <T> CommonResult<T> success(String message, T data) {
        return new CommonResult<>(NykbExceptionEnum.SUCCESS.getCode(),
                message, data);
    }

    public static <T> CommonResult<T> failed() {
        return new CommonResult<>(NykbExceptionEnum.SERVER_ERROR.getCode(),
                NykbExceptionEnum.SERVER_ERROR.getMessage(), null);
    }

    public static <T> CommonResult<T> failed(NykbException e) {
        return new CommonResult<>(e.getCode(),
                e.getMessage(), null);
    }

    public static <T> CommonResult<T> failed(String message) {
        return new CommonResult<>(NykbExceptionEnum.SERVER_ERROR.getCode(),
                message, null);
    }

    public static <T> CommonResult<T> failed(NykbExceptionEnum exceptionEnum) {
        return new CommonResult<>(exceptionEnum.getCode(),
                exceptionEnum.getMessage(), null);
    }
}

ErrorUtil

/**
 * 异常工具类
 */
public class ErrorUtil {

    /**
     * 将栈错误信息转成String返回
     */
    public static String getErrorCause(Throwable t) {
        try {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            t.printStackTrace(pw);
            pw.flush();
            sw.flush();
            return sw.toString();
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }
}

开始使用

如果启动器和异常处理中心不在同一个包,还需要在启动器上添加注解:

@ComponentScan(basePackages = "这里写公共包")

image-1660314480126

空指针异常

    @GetMapping("get")
    public CommonResult<String> get() {
        String s = null;
        String[] strings = s.split("/");
        return CommonResult.success();
    }

image-1660314956705

数据校验异常

控制器上需要加上注解@Validated

    @GetMapping("get")
    public CommonResult<String> get(@NotBlank(message = "姓名不能为空") String name) {
        return CommonResult.success(name);
    }

image-1660315137363

缺少参数异常

    @GetMapping("get")
    public CommonResult<String> get(@RequestParam(value = "name")@NotBlank(message = "姓名不能为空") String name) {
        return CommonResult.success(name);
    }

image-1660315264230

自定义异常

模拟验证登录信息,对于未登录的情况直接抛出异常

    @GetMapping("get")
    public CommonResult<String> get() {
        throw new NykbException(NykbExceptionEnum.NOT_AUTH);
    }

image-1660316126520

还有其他抛异常不再一一举例

    @GetMapping("get")
    public CommonResult<String> get() {
        throw new NykbException("未知的内部错误,请联系开发人员!");
    }

image-1660316281870