@ControllerAdvice,是Spring3.2提供的新注解,它是一个Controller增强器,可对controller中被 @RequestMapping注解的方法加一些逻辑处理。最常用的就是异常处理

@ModelAttribute@ExceptionHandler,前者可以往请求的Model里加数据,后者可以接受请求处理方法抛出的异常。但是他们放在控制器(Controller)里的时候,作用范围是有限的,只管当前控制器里的方法。如果你有几百个控制器,在每个控制器里都加上类似的代码,不免有点冗余和费劲儿。Spring框架提供了@ControllerAdvice注解,帮助你将其应用到所有的控制器上。

Controller Advice字面上意思是“控制器通知”,Advice除了“劝告”、“意见”之外,还有“通知”的意思。你可以将@ModelAttribute@ExceptionHandler标记的方法提取出来,放到一个类里,并将加上@ControllerAdvice,这样,所有的控制器都可以用了:

img

因为@ControllerAdvice被元注解@Component标记,所以它也是可以被组件扫描扫到并放入Spring容器的。

如果你只想对一部分控制器添加通知,比如某个包下的控制器,可以这样写:

img

如果你不想把包名写死,不如把包里的某个类传进去,这样包名重构了也不怕:

img

如果你只想对某几个控制器添加通知,可以这样写:

img

挺简单的是不是?

通知里的@ModelAttribute方法会先于控制器里执行。通知里的@ExceptionHandler方法优先级会低于控制器里的。仔细想想就能明白,局部的要优先于全局的。

控制器通知还有一个兄弟,@RestControllerAdvice,如果用了它,错误处理方法的返回值不会表示用的哪个视图,而是会作为HTTP body处理,即相当于错误处理方法加了@ResponseBody注解。

统一异常处理

需要配合@ExceptionHandler使用。当将异常抛到controller时,可以对异常进行统一处理,规定返回的json格式或是跳转到一个错误页面,如果都放回JSON格式的数据可以使用@RestControllerAdvice

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
48
49
50
51
52
53
54
55
@ControllerAdvice
public class MyExceptionHandler {

/**
* 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
* @param binder
*/
@InitBinder
public void initWebBinder(WebDataBinder binder){
//对日期的统一处理
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
//添加对数据的校验
// binder.setValidator();
}

/**
* 把值绑定到Model中,使全局@RequestMapping可以获取到该值
* @param model
*/
@ModelAttribute
public void addAttribute(Model model) {
model.addAttribute("attribute", "The Attribute");
}

/**
* 捕获CustomException
* @param e
* @return json格式类型 @RestControllerAdvice
*/
@ResponseBody
@ExceptionHandler({CustomException.class}) //指定拦截异常的类型
@ResponseStatus(HttpStatus.NOT_FOUND) //自定义浏览器返回状态码
public Map<String, Object> customExceptionHandler(CustomException e) {
Map<String, Object> map = new HashMap<>();
map.put("code", e.getCode());
map.put("msg", e.getMsg());
return map;
}

/**
* 捕获CustomException 返回一个视图
* @param e
* @return 视图
*/
// @ExceptionHandler({CustomException.class})
// public ModelAndView customModelAndViewExceptionHandler(CustomException e) {
// Map<String, Object> map = new HashMap<>();
// map.put("code", e.getCode());
// map.put("msg", e.getMsg());
// ModelAndView modelAndView = new ModelAndView();
// modelAndView.setViewName("error");
// modelAndView.addObject(map);
// return modelAndView;
// }
}

定义异常类对象

1
2
3
4
5
6
7
8
9
10
11
//自定义异常类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CustomException extends RuntimeException {

private long code;
private String msg;


}

controller中测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Controller
public class ExceptionTestController {

/**
* 关于@ModelAttribute,
* 可以使用ModelMap以及@ModelAttribute()来获取参数值。
*/
@GetMapping("/one")
public String testError(ModelMap modelMap ) {
throw new CustomException(123L, "系统发生500异常!" + modelMap.get("attribute"));
}

@GetMapping("/two")
public String testTwo(@ModelAttribute("attribute") String attribute) {
throw new CustomException(324L, "系统发生500异常!" + attribute);
}
}