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
:可以做全局统一异常处理
- SpringMVC提供了
@ControllerAdvice
与@ResponseBody
组成一个合成注解:@RestControllerAdvice
用于返回JSON数据。
步骤
-
创建一个类,并使用
@ControllerAdvice
(如果返回的是JSON数据,则可以使用@RestControllerAdvice
,它结合了@ControllerAdvice
和@ResponseBody
)注解。 -
在类中定义异常处理方法,使用
@ExceptionHandler
注解指定要处理的异常类型。 -
在异常处理方法中,可以返回适当的响应,比如错误信息页面或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
由于Exception
是Throwable
的子类,所以当发生Exception
(及其子类)异常时,会优先匹配到@ExceptionHandler(Exception.class)
的方法,而不是@ExceptionHandler(Throwable.class)
的方法。因为Exception
比Throwable
更具体、更精确。
@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中,Throwable
和Exception
是继承关系,Throwable
是Exception
的父类。Throwable
有两个直接子类:Exception
和Error
。因此,Throwable
是最大的异常类,它包含了所有的异常和错误。
在Spring的异常处理中,使用@ExceptionHandler
注解可以处理特定类型的异常。当多个异常处理方法可以匹配同一个异常时,最精确的匹配将被选择。
只有当发生的异常不是Exception
的子类(比如Error
)时,才会匹配到Throwable
的方法。但是注意,Spring的异常处理通常只关注Exception
,因为Error
是系统错误,一般不需要我们捕获处理,并且发生Error
异常程序可能即将崩溃。
所以,这两个方法的区别在于:
exception
方法会捕获所有Exception
及其子类的异常(包括运行时异常和非运行时异常),但不会捕获Error
。
throwable
方法会捕获Throwable
及其子类,包括Exception
和Error
。
但是,由于Exception
是Throwable
的子类,并且Exception
的处理方法更具体,所以当Exception
发生时,只会被exception
方法捕获,而不会进入throwable
方法。
如果发生了一个Error
(比如OutOfMemoryError
),那么由于Error
不是Exception
的子类,所以不会进入exception
方法,而是进入throwable
方法。
在实际应用中,我们通常不会去捕获Error
,因为Error
是程序无法处理的严重错误。所以,一般情况下,我们只需要处理Exception
就足够了。