java中统一异常处理,如何实现全局异常处理,@RestControllerAdvice 注解实现统一异常处理

一、粗谈统一异常处理

先看个案例:

1、写了一个接口:

实体类如下:

@Data
public class Emp {
    @NotNull(message = "id 不能为 null")
    private Integer id;

    @NotNull(message = "name 不能为 null")
    private String name;
}

接口如下:

@RestController
public class EmpController {
    
    @PostMapping("/emp")
    public String createEmp(@Valid @RequestBody Emp emp) {
       return "ok";
    }
}

测试时,假如不传 id、name,会抛出 MethodArgumentNotValidException 异常。使用 BindingResult 可以处理异常信息,但 通常使用统一异常处理

① 使用 BindingResult:

@RestController
public class EmpController {
    @PostMapping("/emp")
    public Map createEmp(@Valid @RequestBody Emp emp, BindingResult result) {
        if (result.hasErrors()) {
            Map<String, String> map = new HashMap<>();
            // 获取校验结果,遍历获取捕获到的每个校验结果
            result.getFieldErrors().forEach(item -> {
                // 获取校验的信息
                String message = item.getDefaultMessage(); // 也获取message的值
                String field = item.getField(); //获取属性名
                // 存储得到的校验结果
                map.put(field, message);
            });
            return map;
        }
        return "ok";
    }
}

问题:通过上面的步骤,已经可以捕获异常、处理异常,但是每次都是在业务方法中手动处理逻辑,这样的实现,代码肯定会冗余。可以将其抽出,使用 统一异常处理,每次异常发生时,将其捕获。

② 全局统一异常处理:@RestControllerAdvice、@ExceptionHandler

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Map handlerValidException(MethodArgumentNotValidException e) {
        BindingResult result = e.getBindingResult();
        Map<String, String> map = new HashMap<>();
        // 获取校验结果,遍历获取捕获到的每个校验结果
        result.getFieldErrors().forEach(item ->{
            // 存储得到的校验结果
            map.put(item.getField(), item.getDefaultMessage());
        });
        return map;
    }
}

Controller 中不需要再用 BindingResult 去处理数据了。

二、细谈统一异常处理:结合统一结果返回类

提示:统一结果返回类,可查看这篇文章:点击查看

为什么使用统一异常处理?

使用统一结果处理时,有些异常我们可以提前预知并处理,但是一个运行时异常,我们不一定能预知并处理,这时可以使用统一异常处理,当异常发生时,触发该处理操作,从而保证程序的健壮性。

怎么做?如何处理?代码实现?
  • 使用 @ControllerAdvice 或者 @RestControllerAdvice 注解作为统一异常处理的核心。

    这两个注解都是 Spring MVC 提供的

@ControllerAdvice 和 **@RestControllerAdvice **的区别:

@RestControllerAdvice 注解包含了 @ControllerAdvice@ResponseBody 注解。类似于 @Controller@RestController 的区别。

步骤: 使用 @ControllerAdvice 或者 @RestControllerAdvice 注解标记一个 全局异常处理类。全局异常处理类内部使用 @ExceptionHandler 注解去捕获异常。

可选操作:可以自定义一个异常信息收集类,用于处理项目中的异常,并收集异常信息。

代码实现:

1、可选操作:自定义一个异常类,用于处理项目中的异常,并收集异常信息。

/**
 * 自定义异常类
 * 可以自定义 异常信息message、响应状态码 code(默认为500)
 */
@Data
public class GlobalException extends RuntimeException {
    // 保存异常信息
    private String message;
    
    // 保存响应状态码(默认为500)
    private Integer code = HttpStatus.SC_INTERNAL_SERVER_ERROR;
    
    // 默认构造方法,根据异常信息   构建一个异常实例对象
    public GlobalException(String message) {
        super(message);
        this.message = message;
    }
    
  	// 根据异常信息、响应状态码   构建一个异常实例对象
    public GlobalException(String message, Integer code) {
        super(message);
        this.message = message;
        this.code = code;
    }
    
    // 根据异常信息、异常对象      构建一个异常实例对象
    public GlobalException(String message, Throwable e) {
        super(message, e);
        this.message = message;
    }
    
    // 根据异常信息、响应状态码、异常对象    构建一个异常实例对象
    public GlobalException(String message, Integer code, Throwable e) {
        super(message, e);
        this.message = message;
        this.code = code;
    }
}

2、全局异常处理类:

  • 定义一个全局的异常处理类 GlobalExceptionHandler。使用 @RestControllerAdvice 注解标记这个类。
  • 内部使用 @ExceptionHandler 注解去捕获异常。
// 使用统一结果返回类:Result
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    

    // 处理 Exception 异常
    @ExceptionHandler(Exception.class)
    public Result handlerException(Exception e) {
        log.error(e.getMessage(), e);
        return Result.error().message("系统异常");
    }

    // 处理空指针异常
    @ExceptionHandler(NullPointerException.class)
    public Result handlerNullPointerException(NullPointerException e) {
        log.error(e.getMessage(), e);
        return Result.error().message("空指针异常");
    }

    // 处理自定义异常

    @ExceptionHandler(GlobalException.class)
    public Result handlerGlobalException(GlobalException e) {
        log.error(e.getMessage(), e);
        return Result.error().message(e.getMessage()).code(e.getCode());
    }
}

3、使用:修改某个 controller 如下所示:

@GetMapping("/test")
public Result test(Integer id) { 
    if (id == 0) {
        throw new NullPointerException();
    }
    if (id == -1) {
        throw new GlobalException("参数异常", 400);
    }
    if (id == 1) {
        throw new GlobalException("未查询到结果,请确认输入是否正确");
    }
    return Result.ok().data("items", 123456).message("查询成功");
}

参数为 0 时,抛出 空指针异常。

{
    "success": false,
    "code": 500,
    "message": "空指针异常",
    "data": {}
}

参数为 -1 时,抛出自定义异常并处理。

{
    "success": false,
    "code": 400,
    "message": "参数异常",
    "data": {}
}

参数为 1 时,抛出自定义异常并处理。

{
    "success": false,
    "code": 500,
    "message": "未查询到结果,请确认输入是否正确",
    "data": {}
}

查询成功时,正确处理并返回。

至此,文章结束!!!记得关注哦!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小学鸡!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值