Java 自定义全局异常处理类

自定义全局异常处理类:优雅管理应用错误,告别杂乱代码

感觉本篇对你有帮助可以关注一下我的微信公众号(深入浅出谈java),会不定期更新知识和面试资料、技巧!!!

优点:

自定义全局异常处理类在开发中有以下几个主要用途和好处:

  1. 集中管理异常:通过自定义全局异常处理类,可以集中处理应用程序中所有的异常,而不需要在每个控制器中重复编写异常处理逻辑。这有助于代码的整洁性和可维护性。
  2. 统一的异常响应:全局异常处理类可以统一格式化异常的返回结果,确保前端接收到的一致性和规范性,这样在处理错误时,用户体验更加友好。
  3. 细粒度控制:可以根据不同类型的异常,定制不同的处理方式。例如,可以根据业务逻辑的需要返回不同的状态码和错误信息,方便前端进行处理。
  4. 记录日志:在全局异常处理类中,可以统一记录异常信息,便于后续的排查和分析。这有助于提高系统的监控和维护能力。
  5. 简化代码:通过自动化处理一些常见的异常(如请求参数异常、认证异常等),可以减少每个处理方法中的异常处理代码,使得业务逻辑更加清晰。
  6. 增强安全性:通过统一处理异常,可以有效避免敏感信息(如堆栈跟踪信息等)泄露到客户端,提高了应用程序的安全性。

全局异常处理实现原理

  1. @ControllerAdvice:声明全局异常处理类,监控所有Controller的异常。
  2. @ExceptionHandler:标注具体处理异常的方法,支持按异常类型匹配。
  3. 统一响应体:封装错误码、错误信息、数据等标准结构。

代码实现部分

2.1、定义全局异常处理类

在 Spring Boot 中,可以使用 @ControllerAdvice 注解来定义全局异常处理类

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

/**
 * @Classname GlobalExceptionHandlerAdvice
 * @Description 自定义全局异常处理类
 * @Version 1.0.0
 * @Date 2024/6/6 16:02
 * @Created by Administrator
 */
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandlerAdvice {

    /**-------- 通用异常处理方法 --------**/
    @ExceptionHandler(Exception.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public R error(Exception e) {
        e.printStackTrace();
        log.error("全局异常捕获:" + e);

        return R.fail();    // 通用异常结果
    }

    /**-------- 指定异常处理方法 --------**/
    @ExceptionHandler(NullPointerException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public R error(NullPointerException e) {
        e.printStackTrace();
        log.error("全局异常捕获:" + e);
        return R.setResult(ResultCodeEnum.NULL_POINT);
    }
    /**-------- 下标越界处理方法 --------**/
    @ExceptionHandler(IndexOutOfBoundsException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public R error(IndexOutOfBoundsException e) {
        e.printStackTrace();
        log.error("全局异常捕获:" + e);
        return R.setResult(ResultCodeEnum.INDEX_OUT_OF_BOUNDS);
    }

    /**
     * 方法参数校验
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.error(e.getMessage(), e);
        return R.setResult(ResultCodeEnum.PARAM_ERROR).message(e.getBindingResult().getFieldError().getDefaultMessage());
    }


    /**-------- 自定义定异常处理方法 --------**/
    @ExceptionHandler(UserException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    public R error(UserException e) {
        e.printStackTrace();
        log.error("全局异常捕获:" + e);
        return R.fail().message(e.getMessage()).code(e.getCode());
    }
}

2.2 自定义定异常处理方法

这里我们可以定义自己的异常方法(UserException 类),可后续在业务代码中使用

/**
 * 自定义异常类
 *
 * @author admin
 * @date 2018/10/15
 */
public class UserException extends RuntimeException {
    private Integer code;

    public UserException(String message) {
        super(message);
        this.code = ResultCodeEnum.CUSTOM_ERROR.getCode();
    }

    public UserException(Integer code, String message) {
        super(message);
        this.code = code;
    }

    public UserException(ResultCodeEnum resultCodeEnum) {
        super(resultCodeEnum.getMessage());
        this.code = resultCodeEnum.getCode();
    }

    public String toString() {
        return "UserException{code=" + this.code + ", message=" + this.getMessage() + '}';
    }

    public Integer getCode() {
        return this.code;
    }

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

    public boolean equals(final Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof UserException)) {
            return false;
        } else {
            UserException other = (UserException)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                Object this$code = this.getCode();
                Object other$code = other.getCode();
                if (this$code == null) {
                    if (other$code != null) {
                        return false;
                    }
                } else if (!this$code.equals(other$code)) {
                    return false;
                }

                return true;
            }
        }
    }

    protected boolean canEqual(final Object other) {
        return other instanceof UserException;
    }

    public int hashCode() {
        int result = 1;
        Object $code = this.getCode();
        result = result * 59 + ($code == null ? 43 : $code.hashCode());
        return result;
    }
}

2.3、状态码枚举类

定义要用的状态枚举类,可做到统一的返回格式,这里也是可以自己定义和修改,只不过我提供的 UserException有用到该枚举类

public enum ResultCodeEnum {
    SUCCESS(true, 0, "成功"),
    UNKNOWN_ERROR(false, 999999, "未知错误"),
    DAO_INSERT_ERROR(false, 100000, "插入数据异常"),
    DAO_SELECT_ERROR(false, 100001, "查询数据异常"),
    DAO_UPDATE_ERROR(false, 100002, "更新数据异常"),
    DAO_DELETE_ERROR(false, 100003, "删除数据异常"),
    NULL_POINT(false, 100004, "空指针异常"),
    INDEX_OUT_OF_BOUNDS(false, 100005, "下标越界异常"),
    REQUEST_TIMEOUT(false, 100006, "请求超时"),
    PARAM_ERROR(false, 100007, "参数错误"),
    NOT_INIT_DATA(false, 100008, "数据未初始化"),
    CUSTOM_ERROR(false, 200000, "自定义错误"),
    USER_FORBIDDEN(false, 200001, "用户被禁用"),
    NOT_LOGIN_ERROR(false, 200002, "未登录");

    private Boolean success;
    private Integer code;
    private String message;

    private ResultCodeEnum(boolean success, Integer code, String message) {
        this.success = success;
        this.code = code;
        this.message = message;
    }

    public Boolean getSuccess() {
        return this.success;
    }

    public Integer getCode() {
        return this.code;
    }

    public String getMessage() {
        return this.message;
    }
}


3、使用方法

在需要地方代码中使用:throw new UserException()

@Operation(summary = "文件下载")
    @GetMapping("/download")
    public R download(@RequestParam("fileName") String fileName, HttpServletResponse res) {
        // 我这里就演示一下
        if(ObjectUtil.isEmpty(fileName)){
            throw new UserException("文件名不能为空!");
        }
        minioUtil.download(fileName,res);
        return R.success();
    }

4、测试结果

控制台打印:

2024-09-27T10:12:29.042+08:00 ERROR 20732 --- [eip] [nio-8069-exec-4] c.e.e.c.GlobalExceptionHandlerAdvice     : 全局异常捕获:UserException{code=200000, message=文件名不能为空!}
Disconnected from the target VM, address: '127.0.0.1:62663', transport: 'socket'

参数为空,返回前端就会提示定义的消息

{
  "success": false,
  "code": 200000,
  "message": "文件名不能为空!",
  "data": null,
  "dataList": null,
  "total": 0
}

最后文章有啥不对,欢迎大佬在评论区指点!!!
如果感觉对你有帮助就点赞推荐或者关注一下吧!!!
img****

posted @ 2025-04-24 16:09  古渡蓝按  阅读(95)  评论(0)    收藏  举报