当前位置: 首页 > article >正文

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

文章目录

  • @ResponseBody
  • @ControllerAdvice
  • 最终的异常处理方式

异常的处理分两类
编程式处理:也就是我们的try-catch
声明式处理:使用注解处理
在这里插入图片描述

@ResponseBody


/**
 * 测试声明式异常处理
 */
@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

// 全局异常处理器
//@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)
    实际上做项目的时候:我们编写全局异常处理器,处理所有异常

最终的异常处理方式

前端关心异常状态
后端正确业务流程
推荐:后端只编写正确的业务逻辑,如果出现业务问题,后端通过抛异常的方式提前中断业务逻辑。前端感知异常;
定义一个业务异常


/**
 * 业务异常
 * 大型系统出现以下异常:异常处理文档,固化
 * 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();
    }
}

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

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;
    }


}

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

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

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


    @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);
        }
        ……

http://www.kler.cn/news/328886.html

相关文章:

  • ue4多个面重叠闪烁
  • ubuntu18.04 Anconda安装及使用
  • 【网络安全】-访问控制-burp(1~6)
  • 在idea使用nacos微服务
  • LeetCode[中等] 45. 跳跃游戏 II
  • 排序算法的理解
  • 【ChatGPT】Python 实现计算两线段的变换矩阵
  • 解决Windows远程桌面 “为安全考虑,已锁定该用户账户,原因是登录尝试或密码更改尝试过多,请稍后片刻再重试,或与系统管理员或技术支持联系“问题
  • 师生健康监测系统:SpringBoot技术实践
  • Master PDF Editor 下载及详细安装教程
  • Codeforces Round 976 (Div. 2 ABCDE题)视频讲解
  • Django一分钟:使用prefetch_related避免陷入大量的查询中导致严重的性能问题
  • WebGL深究:动画与交互 —— 赋予虚拟世界生命与灵魂
  • YOLOv11尝鲜测试五分钟极简配置
  • SpringBoot整合JPA详解
  • 工控系统组成与安全需求分析
  • leetcode每日一题day21(24.10.1)——最低票价
  • Street View Synthesis with Gaussian Splatting and Diffusion Prior 学习笔记
  • 【Java SE 题库】移除元素(暴力解法)--力扣
  • 室内定位论文整理-20240925期
  • 计算机毕业设计党建学习网站查看发布党建评论留言搜索部署安装/springboot/javaWEB/J2EE/MYSQL数据库/vue前后分离小程序
  • 【SpringCloud】多机部署, 负载均衡-LoadBalance
  • 使用 Seaborn 热图的 5 种方法(Python 教程)
  • Vue+Flask
  • Pencils Protocol 全面推动市场,生态通证 DAPP 将持续通缩
  • 【数据结构初阶】排序算法(下)冒泡排序与归并排序
  • Jupyter Notebook 产生 jupyter_notebook_config.py 配置文件
  • Html jquery下拉select美化插件——selectFilter.js
  • C++网络编程之IP地址和端口
  • 看似容易赚钱的炒股真的赚钱吗