顾名思义,@ControllerAdvice就是@Controller 的增强版。@ControllerAdvice主要用来处理全局数据,一般搭配@ExceptionHandler、@ModelAttribute以及@InitBinder使用。
@ControllerAdvice最常见的使用场景就是全局异常处理。比如文件上传大小限制的配置,如果用户上传的文件超过了限制大小,就会抛出异常,此时可以通过@ControllerAdvice结合@ExceptionHandler定义全局异常捕获机制,代码如下:
@ControllerAdvice
public class CustomExceptionHandler {
@ExceptionHandler(MaxUploadSizeExceededException.class)
public void uploadException(MaxUploadSizeExceededException e, HttpServletResponse resp) throws IOException {
resp.setContentType("text/html;charset=utf-8");
System.out.println(1111);
PrintWriter out = resp.getWriter();
out.write("上传文件大小超出限制!");
out.flush();
out.close();
}
}
只需在系统中定义CustomExceptionHandler类,然后添加@ControllerAdvice注解即可。当系统启动时,该类就会被扫描到Spring容器中,然后定义uploadException方法,在该方法上添加了@ExceptionHandler注解,其中定义的MaxUploadSizeExceededException.class 表明该方法用来处理MaxUploadSizeExceededException类型的异常。如果想让该方法处理所有类型的异常,只需将MaxUploadSizeExceededException改为 Exception即可。方法的参数可以有异常实例、HttpServletResponse以及HttpServletRequest、Model 等,返回值可以是一段JSON、一个ModelAndView、一个逻辑视图名等。此时,上传一个超大文件会有错误提示给用户。
如果返回参数是一个ModelAndView,假设使用的页面模板为Thymeleaf(注意添加Thymeleaf相关依赖),此时异常处理方法定义如下:
@ControllerAdvice
public class CustomExceptionHandler {
@ExceptionHandler(MaxUploadSizeExceededException.class)
public ModelAndView uploadException(MaxUploadSizeExceededException e) throws IOException {
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "上传文件大小超出限制! ");
mv.setViewName("error");
return mv;
}
}
然后在resources/templates目录下创建error.html文件,内容如下:
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Titletitle>head>
<body>
<div th:text="${msg}">div>
body>
html>
此时上传出错效果一致。
@ControllerAdvice是一个全局数据处理组件,因此也可以在@ControllerAdvice中配置全局数据,使用@ModelAttribute注解进行配置,代码如下:
@ControllerAdvice
public class GlobalConfig {
@ModelAttribute(value = "info")
public Map<String, String> userInfo() {
HashMap<String, String> map = new HashMap<>();
map.put("username", "罗贯中");
map.put("gender", "男");
return map;
}
}
代码解释:
Controller 例代码如下:
public class MyController {
@GetMapping("/hello")
@ResponseBody
public void hello(Model model) {
Map<String, Object> map = model.asMap();
Set<String> keySet = map.keySet();
Iterator<String> iterator = keySet.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
Object value = map.get(key);
System.out.println(key + ">>>>>" + value);
}
}
}
在请求方法中,将Model 中的数据打印出来,如图所示。
@ControllerAdvice结合@InitBinder还能实现请求参数预处理,即将表单中的数据绑定到实体类上时进行一些额外处理。
例如有两个实体类 Book和 Author,代码如下:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
@ToString
public class Book {
private String name;
private String author;
@JsonIgnore//一般标记在属性或者方法上,返回的json数据即不包含该属性
private Float price;
@JsonFormat(pattern = "yyyy-MM-dd")
private Date publicationDate;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Author {
private String name;
private int age;
}
在 Controller 上需要接收两个实体类的数据,Controller 中的方法定义如下:
@ControllerAdvice
public class GlobalConfig1 {
@InitBinder("b")
public void init(WebDataBinder binder) {
binder.setFieldDefaultPrefix("b.");
}
@InitBinder("a")
public void init2(WebDataBinder binder) {
binder.setFieldDefaultPrefix("a.");
}
}
代码解释:
ld设置一个前缀,然后在浏览器中请求http:/ocalhost:8080/book?b.name=三国演义&b.author=罗贯中&a.name=曹雪芹&a.age=48,即可成功地区分出name属性。