Spring之Controller异常处理

在Spring Web后端开发中,对于Controller方法的异常一般都需要特别处理,以防止将异常信息抛给前端或用户。但是如果在各个Controller方法中通过try-catch来捕获处理,不仅繁琐而且代码也不够简洁优雅。这里我们介绍如何通过@ExceptionHandler、@ControllerAdvice注解实现对Controller方法异常的统一处理

abstract.png

@ExceptionHandler 异常处理器注解

该注解标注的方法(即异常处理器),可以对其所在类的中的所有Controller方法(即@RequestMapping注解标注的方法)抛出的异常进行拦截,以便统一处理。同时可在该注解上指定所需捕获的异常类型。同时该方法也支持通过添加@ResponseBody注解来向前端返回请求的响应结果。下述代码即是一个该注解的使用实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@Controller
@RequestMapping("Student")
public class StudentController {
/**
* 异常处理器
* @param e
* @return
*/
@ResponseBody // 通过异常处理器方法统一返回响应结果
@ExceptionHandler(Exception.class)
public String handleException1(Exception e) {
String msg = "Get Exception In handleException1 Method";
System.out.println("msg: " + msg);
return msg;
}

/**
* 运算异常处理器
* @param e
* @return
*/
@ResponseBody // 通过异常处理器方法统一返回响应结果
@ExceptionHandler(ArithmeticException.class)
public String handleException2(ArithmeticException e) {
String msg = "Get ArithmeticException In handleException2 Method";
System.out.println("msg: " + msg);
return msg;
}

@ResponseBody
@RequestMapping("/test1")
public Integer test1(@RequestParam int a) {
int b = 10 / a;
System.out.println("b: " + b);
return b;
}
}
  • 当通过Postman向 http://localhost:8088/Student/test1?a=tony 发送请求,Controller方法由于方法参数类型错误而抛出异常,然后该异常被传递到异常处理器 handleException1 方法中统一进行处理

figure 1.jpeg

  • 当通过Postman向 http://localhost:8088/Student/test1?a=0 发送请求,Controller方法由于除0而抛出ArithmeticException运算异常。Controller方法中抛出的异常会被与异常处理器所指定拦截的异常类型继承关系最近的异常处理器方法所拦截,所以,该运算异常被传递到运算异常处理器 handleException2 方法中统一进行处理

figure 2.jpeg

figure 3.jpeg

@ControllerAdvice 控制器通知注解

虽然异常处理器大大方便了我们对于Controller方法中异常的处理,但是通常在一个项目中有多个Controller类,如果在每个Controller类添加重复的异常处理器方法显然不够简洁优雅。比较容易想到的优化方案是将异常处理器方法放在Controller基类中,其他Controller类通过继承该基类来获得异常处理器,但是由于Java的单继承问题,会使得其无法再继承父类容易产生不便;还有一种优化方案是通过接口的默认方法实现(该特性从JDK 8开始支持),但是这样需要其他Controller类都需要显式地实现该接口,稍微有点麻烦。为此Spring FrameWork提供了一个控制器通知注解——@ControllerAdvice

@ControllerAdvice注解所标识类的异常处理器方法将会对项目中所有标注了@RequestMapping注解的方法生效(即项目中所有的Controller方法),这样我们就可以很方便地统一处理所有Controller方法所抛出的异常,同时保证了Conrotller类的简洁。值得一提的是@ControllerAdvice注解本身已经使用了@Component注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
* 所有Controller方法统一的异常处理:Controller异常处理器通知
*/
@ControllerAdvice
public class ControllerExceptionHandler {

/**
* 异常处理器
* @param e
* @return
*/
@ResponseBody
@ExceptionHandler(Exception.class)
public String handleException1(Exception e) {
String msg = "Get Exception In handleException1 Method";
System.out.println("msg: " + msg);
return msg;
}

/**
* 运算异常处理器
* @param e
* @return
*/
@ResponseBody
@ExceptionHandler(ArithmeticException.class)
public String handleException2(ArithmeticException e) {
String msg = "Get ArithmeticException In handleException2 Method";
System.out.println("msg: " + msg);
return msg;
}
}
...
@Controller
@RequestMapping("Student")
public class StudentController
{
...
@ResponseBody
@RequestMapping("/test1")
public Integer test1(@RequestParam int a) {
int b = 10 / a;
System.out.println("b: " + b);
return b;
}
...
}

参考文献

  1. Spring实战 Craig Walls著、张卫滨译
0%