@ControllerAdvice和@RestControllerAdvice两个注解使用方法基本一致,
两者的区别就是@RestControllerAdvice等价于@ControllerAdvice+@ResponseBody,
RestControllerAdvice方法返回json数据
ControllerAdvice方法返回跳转页面

1、全局异常处理

@ControllerAdvice
public class MyGlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    public ModelAndView customException(Exception e) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("message", e.getMessage());
        mv.setViewName("myerror");
        return mv;
	}
}

自定义全局异常处理时,如果有多个全局异常处理器,则一定要注意加载顺序,如果前面的全局异常处理匹配成功了,则后面的就不会再匹配了

RestControllerAdvice 没有拦截到 @restcontrolleradvice_spring boot


RestControllerAdvice 没有拦截到 @restcontrolleradvice_异常处理_02


如图中,如果myGlobalExceptionHandler中有符合条件的方法,比如异常类型、处理的controller的包路径是否和@ControllerAdvice或者@RestControllerAdvice中的basePackages属性中定义的路径匹配等,如果条件都符合就直接return了,后面的异常处理器就不会再匹配了

RestControllerAdvice 没有拦截到 @restcontrolleradvice_异常处理_03


异常处理器中是否存在入参类型是待处理异常的类型或者其父类的方法,如果有则拿到这个方法用作后续调用这个方法处理异常

RestControllerAdvice 没有拦截到 @restcontrolleradvice_spring_04

2、全局数据绑定

@ControllerAdvice
public class MyGlobalExceptionHandler {
    @ModelAttribute(name = "md")
    public Map<String,Object> mydata() {
        HashMap<String, Object> map = new HashMap<>();
        map.put("age", 99);
        map.put("gender", "男");
        return map;
    }
}

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello(Model model) {
        Map<String, Object> map = model.asMap();
        System.out.println(map);
     	return JSON.toJSONString(map);
    }
}

下面举个例子:

RestControllerAdvice 没有拦截到 @restcontrolleradvice_java_05


RestControllerAdvice 没有拦截到 @restcontrolleradvice_java_06


数据绑定时类型一定要相同,否则会报错

RestControllerAdvice 没有拦截到 @restcontrolleradvice_异常处理_07


如果你请求的参数名能匹配,则数据就是你传入的参数(匹配多少个就赋值多少个,红框下面就是匹配后的输出),否则就是你代码中绑定的数据

RestControllerAdvice 没有拦截到 @restcontrolleradvice_后端_08

3、全局数据预处理

public class Book {
    private String name;
    private Long price;
    //getter/setter
}
public class Author {
    private String name;
    private Integer age;
    //getter/setter
}
@PostMapping("/book")
public void addBook(Book book, Author author) {
    System.out.println(book);
    System.out.println(author);
}

当调用addBook方法时,因为两个实体类都有一个 name 属性,从前端传递时 ,无法区分。此时,通过 @ControllerAdvice 的全局数据预处理可以解决这个问题

给接口中的变量取别名

@PostMapping("/book")
public void addBook(@ModelAttribute("b") Book book, @ModelAttribute("a") Author author) {
    System.out.println(book);
    System.out.println(author);
}

在 @ControllerAdvice 标记的类中添加如下代码:
@InitBinder(“b”) 注解表示该方法用来处理和Book和相关的参数,在方法中,给参数添加一个 b 前缀,即请求参数要有b前缀

@InitBinder("b")
public void b(WebDataBinder binder) {
    binder.setFieldDefaultPrefix("b.");
}
@InitBinder("a")
public void a(WebDataBinder binder) {
    binder.setFieldDefaultPrefix("a.");
}

下面举个例子:

RestControllerAdvice 没有拦截到 @restcontrolleradvice_java_09

RestControllerAdvice 没有拦截到 @restcontrolleradvice_spring boot_10


@InitBinder(“md”)给方法参数绑定前缀

这里的md对应的就是方法中的@ModelAttribute(“md”)声明的md

在属性前添加前缀

RestControllerAdvice 没有拦截到 @restcontrolleradvice_spring_11


RestControllerAdvice 没有拦截到 @restcontrolleradvice_spring_12