概述
SpringMVC 异常统一处理的三种方式:
- 使用 @ExceptionHandler 注解
- 实现 HandlerExceptionResolver 接口
- 使用 @ControllerAdvice + @ExceptionHandler 注解
其中,实现 HandlerExceptionResolver 接口适用于前后端分离项目不使用,用途很少,先不介绍。
@ExceptionHandler注解
作用域:局部,针对一个Controller中的指定异常类。
注有该注解的方法,和发生异常的Mapping方法,两者需要在同一个Controller里面。
@RestController
public class TestExceptionController {
@RequestMapping("/testEx01")
public Map testEx01(){
Map codeMap = new HashMap();
int i = 9/0;
codeMap.put("code",200);
codeMap.put("msg","ok");
return codeMap;
}
// @ExceptionHandler 参数为 要处理器的 异常类(或子类)
@ExceptionHandler(ArithmeticException.class)
public Map exInfo(ArithmeticException e){
Map codeMap = new HashMap();
System.out.println(e.getMessage());
codeMap.put("code",700);
codeMap.put("msg","分母不能为0");
return codeMap;
}
}
使用postman访问url:http://localhost:8080/testEx01。
![](https://img-blog.csdnimg.cn/c178475b7b8f44a0a2f32ce8796d7cc4.png)
后台控制台打印:
/ by zero
@ExceptionHandler(Exception.class) 表示处理所以Exception或其子类。如果我们需要更细的异常控制,我们可以定义多个由@ExceptionHandler 注解的方法,然后对应的Exception.class不一样。
如果我们需要同一个方法处理多种类型的异常:@ExceptionHandler({Exception1.class,Exception2.class})
@ControllerAdvice 注解
作用域:全局,针对全部 Controller 中的指定异常类;该注解需要 和 @ExceptionHandler 配合,来完成全局异常的处理;
注有该注解的方法,和发生异常的Mapping方法,不需要在同一个Controller里面。
全局异常处理类GlobleExceptionAdvice:
//统一异常的通知
@RestControllerAdvice
public class GlobleExceptionAdvice {
@ExceptionHandler(Exception.class)
public Map showInfoE(Exception e){
System.out.println(" 统一异常 ");
System.out.println(e.getMessage());
Map codeMap = new HashMap();
codeMap.put("code",800);
codeMap.put("msg","系统繁忙!!");
return codeMap;
}
// @ExceptionHandler 参数为要处理器的 异常类(或子类)
// 注解参数不声明指定异常类,则默认为方法列表中的异常参数类
@ExceptionHandler(ArithmeticException.class)
public Map showInfo(Exception e){
System.out.println(" 算数异常 ");
System.out.println(e.getMessage());
Map codeMap = new HashMap();
codeMap.put("code",800);
codeMap.put("msg","你不能把分母设置为0");
return codeMap;
}
//自定义异常
@ExceptionHandler(MyException.class)
public Map myException(MyException e){
System.out.println(e.getMsg());
Map codeMap = new HashMap();
codeMap.put("code",e.getCode());
codeMap.put("msg",e.getMsg());
return codeMap;
}
}
@ControllerAdvice 和 @RestControllerAdvice 是作用在类上的注解,@RestControllerAdvice = @ControllerAdvice + @ResponseBody。
自定义异常类MyException,需要继承 RuntimeException:
//自定义异常
public class MyException extends RuntimeException{
private int code;
private String msg;
public MyException() {
}
public MyException(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() { return code; }
public void setCode(int code) { this.code = code; }
public String getMsg() { return msg; }
public void setMsg(String msg) { this.msg = msg; }
}
控制类TestExceptionController 增加 @RequestMapping(“/testEx03”):
@RestController
public class TestExceptionController {
...
//测试 自定义异常
@RequestMapping("/testEx03")
public Map zdyEx(int x,int y){
Map codeMap = new HashMap();
if (y == 1001) {
throw new MyException(1001,"自定义异常");
}
int i = x/y;
codeMap.put("code",200);
codeMap.put("msg","ok");
codeMap.put("data",i);
return codeMap;
}
}
- 使用postman访问url:http://localhost:8080/testEx03?x=1&y=。
![](https://img-blog.csdnimg.cn/34f278fe67f8453da94ccce5f27ec524.png)
后台控制台打印:
统一异常
Failed to convert value of type [java.lang.String] to required type [int]; nested exception is java.lang.NumberFormatException: For input string: ""
因为参数 y 没有传值,会触发 Exception 异常,故返回 code = 800。
- 使用postman访问url:http://localhost:8080/testEx03?x=1&y=1001。
![](https://img-blog.csdnimg.cn/70aa7ca983554f059883e3978849330e.png)
后台控制台打印:
自定义异常
因为参数 y 传值1001,会触发 MyException异常,故返回 code = 1001。
- 使用postman访问url:http://localhost:8080/testEx03?x=1&y=0。
![](https://img-blog.csdnimg.cn/b6385427e34d4aabab2e1bb0c064695d.png)
后台控制台打印:
/ by zero
当某个 Mapping 发生异常后,先由所在 Controller 的 @ExceptionHandler 处理;如果所在 Controller 没有 @ExceptionHandler,则由全局 @ControllerAdvice + @E…Handle 处理;如果没有上者,则由 HandlerExceptionResolver 处理。
所以上例中会调用 TestExceptionController 中的@ExceptionHandler 处理,返回 code = 700。
如果去除TestExceptionController 中的@ExceptionHandler,则会调用全局的GlobleExceptionAdvice。
@RestController
public class TestExceptionController {
@RequestMapping("/testEx01")
public Map testEx01(){
Map codeMap = new HashMap();
int i = 9/0;
codeMap.put("code",200);
codeMap.put("msg","ok");
return codeMap;
}
// @ExceptionHandler 参数为 要处理器的 异常类(或子类)
/*@ExceptionHandler(ArithmeticException.class)
public Map exInfo(ArithmeticException e){
Map codeMap = new HashMap();
System.out.println(e.getMessage());
codeMap.put("code",700);
codeMap.put("msg","分母不能为0");
return codeMap;
}*/
//测试 自定义异常
@RequestMapping("/testEx03")
public Map zdyEx(int x,int y){
Map codeMap = new HashMap();
if (y == 1001) {
throw new MyException(1001,"自定义异常");
}
int i = x/y;
codeMap.put("code",200);
codeMap.put("msg","ok");
codeMap.put("data",i);
return codeMap;
}
}
- 使用postman访问url:http://localhost:8080/testEx03?x=1&y=0。
![](https://img-blog.csdnimg.cn/316525a5f8584267ba35c2d2925389f6.png)
后台控制台打印:
算数异常
/ by zero
@ControllerAdvice 执行顺序
应用中有多个 @ControllerAdvice 或 @RestControllerAdvice 的时候,其顺序默认是按照bean的名称来执行的,如果需要自己指定,可以使用 @Order 注解来指定。建议指定该顺序。
小结
- SpringMVC 异常统一处理的三种方式:使用 @ExceptionHandler 注解;实现 HandlerExceptionResolver 接口;使用 @ControllerAdvice 注解。
- 当某个 Mapping 发生异常后,先由所在 Controller 的 @ExceptionHandler 处理;如果所在 Controller 没有 @ExceptionHandler,则由全局 @ControllerAdvice + @E…Handle 处理;如果没有上者,则由 HandlerExceptionResolver 处理。