Appearance
Spring MVC 全局异常处理
本文主要介绍如何使用Spring MVC提供的工具进行全局异常处理。参考内容:
1. 环境搭建
首先准备测试的Controller:
java
@RestController
public class TestController {
@GetMapping("/calc")
public R calc(@RequestParam("num1") int num1,
@RequestParam("num2") int num2)
{
int result = num1 / num2;
return R.ok(result);
}
}java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class R<T> {
private int code;
private String msg;
@JsonRawValue
private T data;
public static <T> R ok(T data){
return new R<T>(1, "success", data);
}
public static <T> R error(String msg, T data){
return new R<T>(-1, msg, data);
}
public static <T> R error(int code, String msg, T data){
return new R<T>(code, msg, data);
}
}启动应用,访问/calc接口,效果如下:

但是,在我们的程序中没有处理除零异常,如果num2传零,那么会有如下报错:

可以看到如果程序出现异常,Spring MVC有一个默认的异常返回值。
2. 类级别的异常处理器
我们可以在Controller里面提供异常处理器(由@ExceptionHandler标注的方法),当该类中的接口抛出异常时,会由该方法接受异常并进行处理和返回。
java
@RestController
public class TestController {
@GetMapping("/calc")
public R calc(@RequestParam("num1") int num1,
@RequestParam("num2") int num2)
{
int result = num1 / num2;
return R.ok(result);
}
@ExceptionHandler(ArithmeticException.class)
private R handleArithmeticException(ArithmeticException e){
return R.error("数学运算异常:" + e.getMessage(), e);
}
@ExceptionHandler(Throwable.class)
private R handleException(Throwable e){
return R.error(e.getMessage(), e);
}
}测试效果如下:

在@ExceptionHandler注解中,我们可以指定异常的类型(可多个),只有程序抛出特定的异常后才进入该方法执行。同时,我们也可以提供一个默认的异常处理器@ExceptionHandler(Throwable.class),用于处理其他未指定的异常。
3.全局异常处理器
如果在一个大项目中,为每一个Controller指定异常处理器,那会是一项非常重复的工作。所以我们可以配置全局异常处理器(将Controller中的异常处理方法移到全局异常处理器中):
java
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
@ExceptionHandler(ArithmeticException.class)
private R handleArithmeticException(ArithmeticException e){
return R.error("数学运算异常:" + e.getMessage(), e);
}
@ExceptionHandler(Throwable.class)
private R handleException(Throwable e){
return R.error(e.getMessage(), e);
}
}- 使用
@ControllerAdvice表示该类是用于增强Controller的; - 注意要使用
@ResponseBody
4. 异常处理最终方式
- 定义异常枚举类:在大项目中,我们可能存在多个模块,需要在枚举类中按模块划分异常;
- 定义业务异常;
- 编写业务代码时,只需要编写正确的逻辑,如果出现预期的问题,需要以抛异常的方式中断逻辑并通知上层;
- 通过全局异常处理器处理异常;
java
public enum BizExceptionEnum {
// 商品模块异常
GOODS_NOT_FOUND(1001, "商品不存在"),
GOODS_OUT_OF_STOCK(1002,"库存不足"),
// 订单模块异常
ORDRE_TIMEOUT(2001, "订单超时"),
ORDER_NOT_FOUND(2002, "订单不存在"),
// 用户模块异常
USER_NOT_FOUND(3001,"用户不存在"),
// 物流模块异常
LOGISTICS_OVER_WEIGHT(4001,"超重");
@Getter
private int code;
@Getter
private String msg;
private BizExceptionEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
}java
@Getter
public class BizException extends Throwable{
private int code;
private String message;
public BizException(int code, String message) {
this.code = code;
this.message = message;
}
public BizException(BizExceptionEnum bizExceptionEnum) {
this.code = bizExceptionEnum.getCode();
this.message = bizExceptionEnum.getMsg();
}
}java
@ExceptionHandler(BizException.class)
private R handleBizException(BizException bizException){
return R.error(bizException.getCode(), "业务异常:" + bizException.getMessage(), bizException);
}之后,在我们的业务逻辑中,可以按照如下方式抛出异常:
java
public Goods updateGoodsById(Goods goods) throws BizException {
// 查询Goods
Goods goods = goodsMapper.getGoodsById(goods.getId());
if(goods == null){
// 未查到抛异常,结束业务逻辑
throw new BizException(BizExceptionEnum.GOODS_NOT_FOUND);
}
// 查到了,执行修改商品的逻辑
...
}