Spring异常处理-@ExceptionHandler-@ControllerAdvice-全局异常处理

文章目录

异常的处理分两类
编程式处理:也就是我们的try-catch
声明式处理:使用注解处理

@ResponseBody

java 复制代码
/**
 * 测试声明式异常处理
 */
@RestController
public class HelloController {


    //编程式的异常处理;
    //如果大量业务都需要加异常处理代码的话,会很麻烦
//        try {
//            //执行业务
//
//        }catch (Exception e){
//            return R.error(100,"执行异常");
//        }
    @GetMapping("/hello")
    public R hello(@RequestParam(value = "i",defaultValue = "0") Integer i) throws FileNotFoundException {
        int j = 10 / i;

//        FileInputStream inputStream = new FileInputStream("D:\\123.txt");
        String s = null;
        s.length();
        return R.ok(j);
    }


    /**
     * 1、如果Controller本类出现异常,会自动在本类中找有没有@ExceptionHandler标注的方法,
     *    如果有,执行这个方法,它的返回值,就是客户端收到的结果
     *  如果发生异常,多个都能处理,就精确优先
     * @return
     */
    @ResponseBody
    @ExceptionHandler(ArithmeticException.class)
    public R handleArithmeticException(ArithmeticException ex){
        System.out.println("【本类】 - ArithmeticException 异常处理");
        return R.error(100,"执行异常:" + ex.getMessage());
    }


    @ExceptionHandler(FileNotFoundException.class)
    public R handleException(FileNotFoundException ex){
        System.out.println("【本类】 - FileNotFoundException 异常处理");
        return R.error(300,"文件未找到异常:" + ex.getMessage());
    }


    @ExceptionHandler(Throwable.class)
    public R handleException02(Throwable ex){
        System.out.println("【本类】 - Throwable 异常处理");
        return R.error(500,"其他异常:" + ex.getMessage());
    }

}

@ExceptionHandler只能处理本类的

所以其他类的报错怎么办呢?

使用@ControllerAdvice

@ControllerAdvice

java 复制代码
// 全局异常处理器
//@ResponseBody   // 结果还是以JSON的形式写出去
//@ControllerAdvice //告诉SpringMVC,这个组件是专门负责进行全局异常处理的
@RestControllerAdvice   // 合成注解
public class GlobalExceptionHandler {

    /**
     * 如果出现了异常:本类和全局都不能处理,
     * SpringBoot底层对SpringMVC有兜底处理机制;自适应处理(浏览器响应页面、移动端会响应json)
     * 最佳实践:我们编写全局异常处理器,处理所有异常
     * <p>
     * 前端关心异常状态,后端正确业务流程。
     * 推荐:后端只编写正确的业务逻辑,如果出现业务问题,后端通过抛异常的方式提前中断业务逻辑。前端感知异常;
     * <p>
     * 异常处理:
     * 1、
     *
     * @param e
     * @return
     */
    @ExceptionHandler(ArithmeticException.class)
    public R error(ArithmeticException e) {
        System.out.println("【全局】 - ArithmeticException 处理");
        return R.error(500, e.getMessage());
    }


    @ExceptionHandler(BizException.class)
    public R handleBizException(BizException e) {
        Integer code = e.getCode();
        String msg = e.getMsg();
        return R.error(code, msg);
    }

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R methodArgumentNotValidException(MethodArgumentNotValidException ex) {
        //1、result 中封装了所有错误信息
        BindingResult result = ex.getBindingResult();

        List<FieldError> errors = result.getFieldErrors();
        Map<String, String> map = new HashMap<>();
        for (FieldError error : errors) {
            String field = error.getField();
            String message = error.getDefaultMessage();
            map.put(field, message);
        }

        return R.error(500, "参数错误", map);
    }

    // 最终的兜底
    @ExceptionHandler(Throwable.class)
    public R error(Throwable e) {
        System.out.println("【全局】 - Exception处理" + e.getClass());
        return R.error(500, e.getMessage());
    }

}

异常处理优先级

  • 本类 > 全局
  • 精确 > 模糊
    如果出现了异常,本类和全局都不能处理,SpringMVC会兜底处理机制: 自适应处理(什么样的客户端返回什么,要是浏览器就返回一个错误页面,要是客户端,比如Postman,返回json)
    实际上做项目的时候:我们编写全局异常处理器,处理所有异常

最终的异常处理方式

前端关心异常状态
后端正确业务流程

推荐:后端只编写正确的业务逻辑,如果出现业务问题,后端通过抛异常的方式提前中断业务逻辑。前端感知异常;

定义一个业务异常

java 复制代码
/**
 * 业务异常
 * 大型系统出现以下异常:异常处理文档,固化
 * 1、订单  1xxxx
 *      10001 订单已关闭
 *      10002 订单不存在
 *      10003 订单超时
 *      .....
 * 2、商品  2xxxx
 *       20001 商品已下架
 *       20002 商品已售完
 *       20003 商品库存不足
 *       ......
 * 3、用户
 *      30001 用户已注册
 *      30002 用户已登录
 *      30003 用户已注销
 *      30004 用户已过期
 *
 * 4、支付
 *      40001 支付失败
 *      40002 余额不足
 *      40003 支付渠道异常
 *      40004 支付超时
 *
 * 5、物流
 *      50001 物流状态错误
 *      50002 新疆得加钱
 *      50003 物流异常
 *      50004 物流超时
 *
 * 异常处理的最终方式:
 * 1、必须有业务异常类:BizException
 * 2、必须有异常枚举类:BizExceptionEnume  列举项目中每个模块将会出现的所有异常情况
 * 3、编写业务代码的时候,只需要编写正确逻辑,如果出现预期的问题,需要以抛异常的方式中断逻辑并通知上层。
 * 4、全局异常处理器:GlobalExceptionHandler;  处理所有异常,返回给前端约定的json数据与错误码
 */

@Data
public class BizException extends RuntimeException {

    private Integer code; //业务异常码
    private String msg; //业务异常信息
    public BizException(Integer code, String message) {
        super(message);
        this.code = code;
        this.msg = message;
    }

    public BizException(BizExceptionEnume exceptionEnume) {
        super(exceptionEnume.getMsg());
        this.code = exceptionEnume.getCode();
        this.msg = exceptionEnume.getMsg();
    }
}

为了便于管理,我们把所有的异常码和异常信息写一个枚举类。

java 复制代码
public enum BizExceptionEnume {

    // ORDER_xxx:订单模块相关异常
    // PRODUCT_xxx:商品模块相关异常
    // 动态扩充.....

    ORDER_CLOSED(10001, "订单已关闭"),
    ORDER_NOT_EXIST(10002, "订单不存在"),
    ORDER_TIMEOUT(10003, "订单超时"),
    PRODUCT_STOCK_NOT_ENOUGH(20003, "库存不足"),
    PRODUCT_HAS_SOLD(20002, "商品已售完"),
    PRODUCT_HAS_CLOSED(20001, "商品已下架");

    @Getter
    private Integer code;
    @Getter
    private String msg;


    private BizExceptionEnume(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }


}

然后在我们的全局异常处理器中处理我们的业务异常

java 复制代码
 @ExceptionHandler(BizException.class)
    public R handleBizException(BizException e) {
        Integer code = e.getCode();
        String msg = e.getMsg();
        return R.error(code, msg);
    }

在业务中就可以这样用了。

java 复制代码
    @Override
    public void updateEmp(Employee employee) {

        //防null处理。考虑到service是被controller调用的;
        //controller层传过来的employee 的某些属性可能为null,所以先处理一下
        //怎么处理?
        Long id = employee.getId();
        if(id == null){ //页面没有带id
            //中断的业务的时候,必须让上层及以上的链路知道中断原因。推荐抛出业务异常
            throw new BizException(BizExceptionEnume.ORDER_CLOSED);
        }
        ......
相关推荐
Survivor0014 分钟前
高并发系统流量治理的底层算法
java·开发语言
凡人叶枫7 分钟前
Effective C++ 条款35:考虑 virtual 函数以外的其他选择
java·c++·spring
garmin Chen15 分钟前
从 Transformer 到 Agent:大模型技术全景解析
java·人工智能·python·深度学习·transformer
愚公移码20 分钟前
蓝凌EKP18产品:流程引擎技术篇之流程核心概念模型
java·人工智能·流程引擎·蓝凌
Full Stack Developme27 分钟前
Apache Tika 教程
java·开发语言·python·apache
鹅城剑仙42 分钟前
Java线程池完全指南
java
李白的天不白44 分钟前
SmartAdmin(基于 Spring Boot 框架)中配置跨域请求 VUE3 设置请求头
java·前端
橙子进阶之路1 小时前
Java线程(CompletableFuture)
java·开发语言
鹅城剑仙1 小时前
Java CompletableFuture 异步编程完全指南
java
2601_961875241 小时前
法考备考计划表|学习计划|资料已整理
java·开发语言·学习·eclipse·tomcat·c#·hibernate