@ControllerAdvice 可以在 Spring Boot 中实现对一些功能的 AOP,通过 @ExceptionHandler、@ModelAttribute、@InitBinder 等注解来实现全局异常处理、全局数据管理以及参数预处理等功能。
全局异常处理
通过 @ControllerAdvice 与 @ExceptionHandler 就能够构建一个类似拦截器的功能,检测整个程式运行时候所发生的异常,并且根据不同的异常对他们进行相应的处理。比方说,在一个上传文件的功能中,我们设置了一个文件上传大小的限制。如果上传的文件超过了这个设定,就会报出 MaxUploadSizeExceededException
的异常。所以可以添加以下代码:
@ControllerAdvice
public class CustomExceptionHandler {
@ExceptionHandler(MaxUploadSizeExceededException.class)
public void maxUploadSizeExceededExceptionHandler(MaxUploadSizeExceededException e,
HttpServletResponse response) {
response.setContentType("text/html;charset=UTF-8");
PrintWriter writer = null;
try {
writer = response.getWriter();
writer.write("文件上傳大小超過限制!");
writer.flush();
} catch (IOException exception) {
exception.printStackTrace();
} finally {
writer.close();
}
}
}
出错时直接提示错误,当然这绝对不是真实开发中所用的方法,这里可以实现更多不同的解决方法来处理这个异常。
添加全局数据
@ControllerAdvice
注解搭配 @ModelAttribute
将可以添加全局数据,在前者标注的类中所建立的 Model Attribute 可以被所有 Controller 中的 model 读到。
GlobalConfig.java
@ControllerAdvice
public class GlobalConfig {
@ModelAttribute("userInfo")
public Map<String, String> userInfo() {
Map<String, String> map = new HashMap<>();
map.put("username", "andyching");
map.put("gender", "Male");
return map;
}
}
UserController.java
@RestController
public class UserController {
@GetMapping("/getUserInfo")
public Object getUserInfo(Model model) {
return model.getAttribute("userInfo");
}
}
这样就能直接输出之前所保存的内容了。
请求参数预处理
@ControllerAdvice
还可以与 @InitBinder
一同使用,进行参数的预处理。以下有一个例子:
Author.java
public class Author {
private String name;
private String age;
//... 省略 Getter、Setter 与 toString
}
Book.java
public class Book {
private String name;
private String author;
//... 省略 Getter、Setter 与 toString
}
BookController.java
@RestController
public class BookController {
@GetMapping("/book")
public String book(Book book, Author author) {
return book.toString() + ">>>" + author.toString();
}
}
那么,在 url 请求的时候,比如 localhost:8080/book?author=Darvis&age=25
时,就会发现 name
属性将出现冲突,因此这时候就可以使用 @InitBinder
来解决这个冲突:
GlobalConfig.java
@ControllerAdvice
public class GlobalConfig {
@InitBinder("book")
public void initBook(WebDataBinder dataBinder) {
dataBinder.setFieldDefaultPrefix("book.");
}
@InitBinder("author")
public void initAuthor(WebDataBinder dataBinder) {
dataBinder.setFieldDefaultPrefix("author.");
}
}
这时候只要访问时标注它们的值就可以实现功能了:http://localhost:8080/book?book.name=萬有引力&author.name=達爾維斯&age=30&author=達爾維斯
。
另外,如果不想用同属性名的标记的话,可以做以下处理:
更改 BookController.java
@RestController
public class BookController {
@GetMapping("/book")
public String book(@ModelAttribute("b") Book book,
@ModelAttribute("a") Author author) {
return book.toString() + ">>>" + author.toString();
}
}
更改 GlobalConfig.java
为:
@ControllerAdvice
public class GlobalConfig {
@InitBinder("b")
public void initBook(WebDataBinder dataBinder) {
dataBinder.setFieldDefaultPrefix("b.");
}
@InitBinder("a")
public void initAuthor(WebDataBinder dataBinder) {
dataBinder.setFieldDefaultPrefix("a.");
}
}
然后实际的开发中一般不会用这种方式处理,这里就简单复习一下。