前言
接口类项目开发时,为了便于后期查找问题,一般会拦截器或过滤器中记录每个接口请求的参数与响应值记录,
请求参数很容易从request中获取,但controller的返回值无法从response中获取,有一个简单的方法,在controller接口的最后将返回值保存到request域中,这种方法虽然简单,但是开发起来太麻烦,需要在每个controller的最后添加一行代码,且该功能不属于业务功能,不应该接口中去实现,应该有个全局的处理方法。从而引出 ControllerAdvice
注解
ControllerAdvice
是 springmvc controller
增强器,其有三个作用:
ModelAttribute: 暴露@RequestMapping 方法返回值为模型数据:放在功能处理方法的返回值上时,是暴露功能处理方法的返回值为模型数据,用于视图页面展示时使用。
InitBinder : 用于自定义@RequestMapping 方法参数绑定
Exception : 用于@ResponseBody返回值增加处理
而 @ControllerAdvice
定义如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<?>[] assignableTypes() default {};
Class<? extends Annotation>[] annotations() default {};
}
默认情况下,@ControllerAdvice 中的方法全局应用于所有控制器。使用选择器,如注释、basepackageclass和basePackages(或其别名值)来定义目标控制器的更小子集。如果声明了多个选择器,则应用布尔或逻辑,这意味着所选控制器应该匹配至少一个选择器。请注意,选择器检查是在运行时执行的,因此添加许多选择器可能会对性能产生负面影响并增加复杂性。
@ControllerAdvice:对于那些声明了@ExceptionHandler、@InitBinder或@ModelAttribute方法并要在多个@Controller类间共享的类,特殊化@Component。
总的来说,@ControllerAdvice 有其下作用
全局异常处理
全局数据绑定
全局数据预处理
一、全局异常处理
方式一:结果为字符串
先定义接口:
@RestController
public class HelloController {
@GetMapping("/hello")
public void hello(){
System.out.println( 1 / 0);
}
}
接口中存在一个算术异常,在定义一个全局异常处理类,用来处理异常
@RestControllerAdvice//类似 @RestController
public class GlobalException {
@ExceptionHandler(ArithmeticException.class)
public String globalException(ArithmeticException e){//返回字符串
return e.toString();
}
}
@ExceptionHandler 注解都可以在普通的 Controller 类上使用, @ControllerAdvice 只是作用范围可以自定义(默认全部)
当我们访问接口时,返回该异常的信息:
方式二:结果为页面
定义显示异常信息的页面:exception.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>exception-thymeleaf</h1>
<span th:text="${error}"></span>
</body>
</html>
在定义类用来拦截空指针异常:
@ControllerAdvice//类似 controller
public class GlobalException {
@ExceptionHandler(Exception.class)
public ModelAndView globalHtml(Exception e){
ModelAndView mv = new ModelAndView("exception");
mv.addObject("error",e);
return mv;
}
}
在该类中通过 modelandview 来显示页面与将异常信息返回到页面中
定义接口:
@RestController
public class HelloController {
@GetMapping("/hello")
public void hello(){
String[] strs = null;
System.out.println(strs[0]);
}
}
访问接口时:结果如下:
注意:对于@ExceptionHandler方法,在特定通知bean的处理程序方法中,根异常匹配将优于仅匹配当前异常的原因。但是,高优先级通知上的原因匹配仍然优于低优先级通知bean上的任何匹配(无论是根级还是原因级)。因此,请按照相应的顺序在优先级排序的通知bean上声明您的主根异常映射。
因为异常可能在任何时候引发,使模型的内容不可靠。由于这个原因,@ExceptionHandler方法不提供对Model参数的访问。
二、全局数据绑定
首先定义数据类:
@ControllerAdvice
public class GlobalData {
//可通过 ModelAttribute 中的 name属性来设置传输数据名
@ModelAttribute
public Map<String,Object> myData(){
Map<String,Object> map = new HashMap<>();
map.put("name", "阿良");
map.put("age", 20);
return map;
}
}
1、@ModelAttribute :将方法参数或方法返回值绑定到命名模型属性的注释,公开给web视图。支持带有 @RequestMapping 方法的控制器类。
2、@ModelAttribute 还可以通过在控制器类中使用 @RequestMapping 方法注释 accessor 方法来将引用数据暴露给web视图。允许这样的访问器方法具有@RequestMapping 方法支持的任何参数,返回要公开的模型属性值。
在接口中通过 model 来接收数据:
@RestController
public class HelloController {
@GetMapping("/data")
public void mydata(Model model){
Map<String, Object> asMap = model.asMap();
Map<String, Object> map = (Map<String, Object>) asMap.get("map");
Set<String> keys = map.keySet();
for (String key : keys) {
System.out.println(key + "---->" + map.get(key));
}
}
}
其中 因为 @ModelAttribute 没有指定数据的 名称,所以默认为方法返回类型的首字母小写。
控制台成功输出数据。
三、全局数据预处理
首先定义两个model:
//book类
public class Book {
private String name;
private Integer price;
//get、set、tostring
}
//author类
public class Author {
private String name;
private Integer age;
//get、set、tostring
}
定义全局数据绑定类:
@ControllerAdvice
public class GlobalData {
@InitBinder("b")
public void b(WebDataBinder binder){
binder.setFieldDefaultPrefix("b.");
}
@InitBinder("a")
public void a(WebDataBinder binder){
binder.setFieldDefaultPrefix("a.");
}
}
其中 WebDataBinder 作用为:用于从web请求参数到JavaBean对象的数据绑定的特殊DataBinder。专为web环境,但不依赖于Servlet API;作为更具体的DataBinder变体(如ServletRequestDataBinder)的基类。
通过 @InitBinder :标识初始化org.springframework.web.bind.WebDataBinder方法的注释,该方法将用于填充带注释的处理程序方法的命令和表单对象参数。即在请求时,发送数据
在定义接口:
@RestController
public class GlobalDataProcessController {
@PostMapping("/datapro")
public void dataProcess(@ModelAttribute("b") Book book, @ModelAttribute("a") Author author){
System.out.println(book);
System.out.println(author);
}
}
当我们通过接口地址时,可携带数据发送。这里通过 postman 来测试:
这时,控制台输出为:
注意:默认情况下,@ControllerAdvice 中的方法全局应用于所有控制器。使用选择器,如注释、basepackageclass和basePackages(或其别名值)来定义目标控制器的更小子集。如果声明了多个选择器,则应用布尔或逻辑,这意味着所选控制器应该匹配至少一个选择器。请注意,选择器检查是在运行时执行的,因此添加许多选择器可能会对性能产生负面影响并增加复杂性。