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

Spring MVC 封装全局统一异常处理

Spring MVC其他扩展:全局统一异常处理

  • 异常处理
    • 概念定义
      • 定义
      • 异常处理分类
      • 步骤
    • 核心组件
      • @ControllerAdvice
        • 配置选项
          • 1. 指定包范围
          • 2. 指定注解
          • 3. 指定基类
      • @RestControllerAdvice (推荐用于REST API)
      • @ExceptionHandler
    • 代码准备工作
      • 1. 封装统一的【响应体】
      • 2. 封装统一的【业务枚举】
      • 3. ★封装统一的【自定义业务异常】
      • 4. ★创建全局统一异常处理控制器
      • 5. 业务中抛出异常
    • 扩展
      • Throwable与Exception

代码地址:https://gitee.com/hua5h6m/framework-java/tree/master/spring-global-exception-advice

异常处理

概念定义

Spring MVC 全局统一异常处理 是一种设计模式,用于在应用程序层面统一捕获和处理各种异常,避免异常处理代码分散在各个控制器方法中,提供一致的错误响应格式

定义

全局统一异常处理通过使用@ControllerAdvice(或@RestControllerAdvice)和@ExceptionHandler注解来实现。

  • @ControllerAdvice:这是一个组件注解,用于标识一个类作为全局异常处理器。它可以包含多个异常处理方法,这些方法可以处理来自多个控制器的异常。
  • @ExceptionHandler:该注解用于标记一个方法作为异常处理器,并指定该方法能够处理的异常类型。当控制器中抛出指定类型的异常时,Spring会调用对应的异常处理方法。

异常处理分类

  • 编程式异常处理
    • try - catch、throw
  • 声明式异常处理
    • SpringMVC提供了@ExceptionHandler@ControllerAdvice等便捷的声明式注解来进行快速的异常处理
    • @ExceptionHandler:可以处理指定的类型异常
    • @ControllerAdvice:可以集中处理所有controller的异常
    • @ExceptionHandler + @ControllerAdvice:可以做全局统一异常处理

@ControllerAdvice@ResponseBody组成一个合成注解:@RestControllerAdvice用于返回JSON数据。

步骤

  1. 创建一个类,并使用@ControllerAdvice(如果返回的是JSON数据,则可以使用@RestControllerAdvice,它结合了@ControllerAdvice@ResponseBody)注解。

  2. 在类中定义异常处理方法,使用@ExceptionHandler注解指定要处理的异常类型。

  3. 在异常处理方法中,可以返回适当的响应,比如错误信息页面或JSON错误响应。

核心组件

@ControllerAdvice

@ControllerAdvice
public class GlobalExceptionHandler {// 全局异常处理类
}
配置选项
1. 指定包范围
@RestControllerAdvice("com.yourpackage.controller")
public class GlobalExceptionHandler {// 只处理指定包下的控制器异常
}
2. 指定注解
@RestControllerAdvice(annotations = RestController.class)
public class GlobalExceptionHandler {// 只处理带有@RestController注解的控制器
}
3. 指定基类
@RestControllerAdvice(assignableTypes = {BaseController.class})
public class GlobalExceptionHandler {// 只处理BaseController及其子类的异常
}

@RestControllerAdvice (推荐用于REST API)

@RestControllerAdvice
public class GlobalExceptionHandler {// 组合了 @ControllerAdvice + @ResponseBody// 自动将返回值序列化为JSON
}

@ExceptionHandler

@ExceptionHandler(Exception.class)
public R<?> handleException(Exception e) {// 处理特定类型的异常
}

代码准备工作

1. 封装统一的【响应体】

import lombok.Data;@Data
public class R<T> {private Integer code;private String msg;private T data;/*** 成功200** @return R*/public static <T> R<T> success(T data) {R<T> r = new R<>();r.setCode(200);r.setMsg("success");r.setData(data);return r;}/*** 成功200,重载方法,不需要返回** @return R*/public static <T> R<T> success() {R<T> r = new R<>();r.setCode(200);r.setMsg("success");return r;}/*** 异常返回** @param code 错误码* @param msg  异常信息* @return R*/public static <T> R<T> error(Integer code, String msg) {R<T> r = new R<>();r.setCode(code);r.setMsg(msg);return r;}/*** 异常返回** @return R*/public static <T> R<T> error() {R<T> r = new R<>();r.setCode(500);r.setMsg("error");return r;}
}

2. 封装统一的【业务枚举】

/*** 业务异常码* 订单:ORDER_XXX* 用户:USER_XXX*/
public enum BusinessExceptionEnum {// 枚举类,最后一个枚举字段,结束用分号// 其他美剧类型用逗号,。ORDER_CLOSE(10001, "订单已关闭"),ORDER_NOT_EXIST(10002, "订单不存在"),ORDER_TIME(10003, "订单超时");@Getterprivate final Integer code;@Getterprivate final String msg;BusinessExceptionEnum(Integer code, String msg) {this.code = code;this.msg = msg;}
}

3. ★封装统一的【自定义业务异常】

/*** 自定义业务异常*/
public class BusinessException extends RuntimeException {// 业务异常码private Integer code;// 业务异常信息private String msg;public BusinessException(Integer code, String msg) {this.code = code;this.msg = msg;}/*** 接收枚举* @param businessExceptionEnum 自定义业务枚举*/public BusinessException(BusinessExceptionEnum businessExceptionEnum) {super(businessExceptionEnum.getMsg());this.code = businessExceptionEnum.getCode();}
}

4. ★创建全局统一异常处理控制器

在您提供的代码中,有两个异常处理方法:

  • 一个处理Exception.class
  • 一个处理Throwable.class

由于ExceptionThrowable子类,所以当发生Exception(及其子类)异常时,会优先匹配到@ExceptionHandler(Exception.class)的方法,而不是@ExceptionHandler(Throwable.class)的方法。因为ExceptionThrowable更具体、更精确


@RestControllerAdvice
public class GlobalControllerAdvice {/*** 只需要写Exception.class即可* @param e* @return*/@ExceptionHandler(Exception.class)public R exception(Exception e) {return R.error(500, e.getMessage());}/*** 忽略即可,不需要写* @param e* @return*/@ExceptionHandler(Throwable.class)public R throwable(Throwable e) {return R.error(501, e.getMessage());}/*** 业务异常* @param e 自定义业务异常* @return R*/@ExceptionHandler(BusinessException.class)public R businessException(BusinessException e) {return R.error(e.getCode(), e.getMessage());}
}

5. 业务中抛出异常

在业务中抛出自定义异常,并在枚举类中定义异常的编码业务报错信息

@RestController
@RequestMapping("user")
public class UserController {@GetMapping("info")public R info(@RequestParam("id") Integer id) {int j = 1 / id;return R.success(j);}@GetMapping("business")public R business(@RequestParam("id") Integer id) {if (id == 1) {// 业务中抛出自定义异常,并在枚举类中定义异常的编码和业务报错信息throw new BusinessException(BusinessExceptionEnum.ORDER_NOT_EXIST);}return R.success(id);}@ExceptionHandler(ArithmeticException.class)public R exception(Exception e) {return R.error(500, "类内异常" + e.getMessage());}@ExceptionHandler(FileNotFoundException.class)public R fileNotFoundException(FileNotFoundException e) {return R.error(500, "File类内异常" + e.getMessage());}
}

扩展

Throwable与Exception

在Java中,ThrowableException继承关系ThrowableException的父类。Throwable有两个直接子类:ExceptionError。因此,Throwable最大的异常类,它包含了所有的异常和错误

在Spring的异常处理中,使用@ExceptionHandler注解可以处理特定类型的异常。当多个异常处理方法可以匹配同一个异常时,最精确的匹配将被选择

只有当发生的异常不是Exception的子类(比如Error)时,才会匹配到Throwable的方法。但是注意,Spring的异常处理通常只关注Exception,因为Error系统错误,一般不需要我们捕获处理,并且发生Error异常程序可能即将崩溃。

所以,这两个方法的区别在于:

exception方法会捕获所有Exception及其子类的异常(包括运行时异常和非运行时异常),但不会捕获Error

throwable方法会捕获Throwable及其子类,包括ExceptionError
但是,由于ExceptionThrowable的子类,并且Exception的处理方法更具体,所以当Exception发生时,只会被exception方法捕获,而不会进入throwable方法。

如果发生了一个Error(比如OutOfMemoryError),那么由于Error不是Exception的子类,所以不会进入exception方法,而是进入throwable方法。

在实际应用中,我们通常不会去捕获Error,因为Error是程序无法处理的严重错误。所以,一般情况下,我们只需要处理Exception就足够了。

http://www.dtcms.com/a/499516.html

相关文章:

  • 海尔建设网站的内容wordpress设置教程
  • Flutter---EQ均衡器
  • 响应式食品企业网站网站的外链是什么
  • 【Protobuf】proto3语法详解1
  • 网站备案要做家居网站设计
  • VS2022+DirectX9之创建DirectX9设备
  • unordered_map和unordered_set的封装与简单测试
  • (Kotlin协程十六)try/catch 可以捕获子协程的异常吗?为什么?
  • 网站移动端怎么做的做外国网站怎么买空间
  • 图像的脉冲噪声和中值滤波
  • 3.4特殊矩阵的压缩存储
  • SpringAI+DeepSeek大模型应用开发
  • 递归-24.两两交换链表中的节点-力扣(LeetCode)
  • 【Java零基础·第12章】Lambda与Stream API
  • Qemu-NUC980(八):GPIO Controller
  • 外贸型企业网站建设开源商城源码
  • JS逆向-安全辅助项目Yakit热加载魔术方法模版插件语法JSRpc进阶调用接口联动
  • 使用IOT-Tree接入各种数据转BACnet模拟设备输出
  • 网站搭建说明北京海淀区是几环
  • 基于多模态AI技术的传统行业智能化升级路径研究——以开源AI大模型、AI智能名片与S2B2C商城小程序为例
  • 【C语言进阶】指针进阶_数组指针的使用,数组参数和指针参数
  • PySide6 控件插入日期时间(QDateTime)
  • 网站建设 jsp php垂直网站建设
  • 招商网站大全企业官方网站建设的流程
  • 征程 6 | 工具链如何支持 Matmul/Conv 双 int16 输入量化?
  • 【案例实战】鸿蒙分布式调度:跨设备协同实战
  • 中英文网站设计网站开发投标文件
  • Langgraph译文1:让AI自主决策的代理架构
  • 如何让百度能查到自己衡阳专业的关键词优化终报价
  • 为什么.NET的System.IO.Compression无法解压zlib流