上一节讲到springboot异常处理时,使用到了@ControllerAdvice
下面详细讲解讲解下@ControllerAdvice用法,以及Restful风格的@RestControllerAdvice注解使用。
上节文章:Spring Boot(11):异常处理
@ControllerAdvice就是@Controller 的增强版。@ControllerAdvice主要有三个用途:全局异常处理、全局数据绑定、全局数据预处理。一般搭配@ExceptionHandler、@ModelAttribute以及@InitBinder使用。
结合@ExceptionHandler,用来指明异常的处理类型。
参考上节内容。如下,创一个自己的全局异常处理类,代码如下:
/**
* 全局异常处理类
*/
@ControllerAdvice
public class GlobalController {
/**
* 该方法返回ModelAndView:目的是为了可以让我们封装视图和错误信息
* @param e 参数 Exception e:会将产生异常对象注入到方法中
* @return
*/
//拦截的异常可以写Exception
@ExceptionHandler(value = {java.lang.ArithmeticException.class})
public ModelAndView arithmeticExceptionHandler(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("errorMsg",e+"controllerAdvice");
mv.setViewName("error");
return mv;
}
}
在该类中,可以定义多个方法,不同的方法处理不同的异常,,也可以直接向上面代码一样,在一个方法中处理所有的异常。
结合@ModelAttribute注解使用。
全局数据绑定功能可以用来做一些初始化的数据操作,我们可以将一些公共的数据定义在添
加了 @ControllerAdvice 注解的类中,这样,在每一个 Controller 的接口中,就都能
够访问导致这些数据。
首先定义全局数据,如下:
@ControllerAdvice
public class MyGlobalExceptionHandler {
@ModelAttribute(name = "mydata")
public Map<String,Object> mydata() {
HashMap<String, Object> map = new HashMap<>();
map.put("age", 20);
map.put("gender", "男");
return map;
}
}
使用 @ModelAttribute 注解标记该方法的返回数据是一个全局数据,默认情况下,这个全局数据的 key 就是返回的变量名,value 就是方法返回值,也可以通过@ModelAttribute 注解的 name 属性去重新指定 key。
然后,个早Controller 的接口中,可以获取到这里定义的数据:
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
Map<String, Object> map = model.asMap();
System.out.println(map);
return "hello controller advice";
}
}
结合@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;
}
此时,定义一个数据添加接口,如下:
@PostMapping("/book")
public void addBook(Book book, Author author) {
System.out.println(book);
System.out.println(author);
}
这个时候,添加操作就会有问题,因为两个实体类都有一个 name 属性,从前端传递时 ,无法区分。此时,通过 @ControllerAdvice 的全局数据预处理可以解决这个问题。
解决步骤如下:
1.给接口中的变量取别名
@PostMapping("/book")
public void addBook(@ModelAttribute("b") Book book,
@ModelAttribute("a") Author author)
{
System.out.println(book);
System.out.println(author);
}
2.进行请求数据预处理
@ControllerAdvice
public class GlobalConfig1 {
@InitBinder("b")
public void init(WebDataBinder binder) {
binder.setFieldDefaultPrefix("b.");
}
@InitBinder("a")
public void init2(WebDataBinder binder) {
binder.setFieldDefaultPrefix("a.");
}
}
在 GlobalConfig类中创建两个方法,第一个@InitBinder(“b”)表示该方法是处理@ModelAttribute(“b”)对应的参数的,第二个@InitBinder(“a”)表示该方法是处理@ModelAttribute(“a”)对应的参数的。
@InitBinder(“b”) 注解表示该方法用来处理和 Book 和相关的参数,在方法中,给参数添加一个 b 前缀,即请求参数要有 b 前缀。
3.发送请求
请求发送时,通过给不同对象的参数添加不同的前缀,可以实现参数的区分,如下:
http:/localhost:8080/book?b.name=三国演义&b.author=罗贯中&a.name=曹雪芹&a.age=48,即可成功地区分出name属性。
@RestControllerAdvice是一个组合注解,由@ControllerAdvice、@ResponseBody组成,用于Restful风格的controller。
@RestControllerAdvice注解将作用在所有注解了@RequestMapping的控制器的方法上。
@RestControllerAdvice用法上与@ControllerAdvice没什么不同,也可与@ExceptionHandler,@InitBinder和@ModelAttribute注解结合使用。
下面有个简单示例:
@ControllerAdvice
public class GlobalController{
// 全局数据绑定
@ModelAttribute
public void addUser(Model model) {
model.addAttribute("msg", "全局数据");
}
// 全局数据预处理
@InitBinder("user")
public void initBinder(WebDataBinder binder) {
}
// 全局异常处理
@ExceptionHandler(Exception.class)
public String handleException(Exception e) {
return "error";
}
}
@ControllerAdvice源码如下:
package org.springframework.web.bind.annotation;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;
@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 {};
}
可以看到,有几个属性,其中:
1.basePackages
指定一个或多个包,这些包及其子包下的所有 Controller 都被该 @ControllerAdvice 管理。
@RestControllerAdvice(basePackages={"com.example.spring"})
@Slf4j
public class ExceptionHandlerAdvice {
@ExceptionHandler(Exception.class)
public String handleException(Exception e) {
return "error";
}
}
2.basePackageClasses
是 basePackages 的一种变形,指定一个或多个 Controller 类,这些类所属的包及其子包下的所有 Controller 都被该 @ControllerAdvice 管理。
@RestControllerAdvice(basePackageClasses={TestController.class})
@Slf4j
public class ExceptionHandlerAdvice {
@ExceptionHandler(Exception.class)
public String handleException(Exception e) {
return "error";
}
}
3.assignableTypes
指定一个或多个 Controller 类,这些类被该 @ControllerAdvice 管理。
4.annotations
指定一个或多个注解,被这些注解所标记的 Controller 会被该 @ControllerAdvice 管理。
@ControllerAdvice(annotations = {TestAnnotation.class})
@Slf4j
public class ExceptionHandlerAdvice {
@ExceptionHandler(Exception.class)
public String handleException(Exception e) {
return "error";
}
}
参考:https://blog.csdn.net/user2025/article/details/105458842
https://blog.csdn.net/qq_43581790/article/details/123871439