• springboot(12):@ControllerAdvice和@RestControllerAdvice注解使用


    上一节讲到springboot异常处理时,使用到了@ControllerAdvice
    下面详细讲解讲解下@ControllerAdvice用法,以及Restful风格的@RestControllerAdvice注解使用。
    上节文章:Spring Boot(11):异常处理

    @ControllerAdvice注解

    @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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在该类中,可以定义多个方法,不同的方法处理不同的异常,,也可以直接向上面代码一样,在一个方法中处理所有的异常。

    全局数据绑定

    结合@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; 
    	} 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    使用 @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"; 
    	} 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    全局数据预处理

    结合@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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    此时,定义一个数据添加接口,如下:

    @PostMapping("/book")
    public void addBook(Book book, Author author) {
    	System.out.println(book);
    	System.out.println(author);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这个时候,添加操作就会有问题,因为两个实体类都有一个 name 属性,从前端传递时 ,无法区分。此时,通过 @ControllerAdvice 的全局数据预处理可以解决这个问题。
    解决步骤如下:

    1.给接口中的变量取别名

    @PostMapping("/book")
    public void addBook(@ModelAttribute("b") Book book,
    					@ModelAttribute("a") Author author) 
    {
    	System.out.println(book);
    	System.out.println(author);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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.");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在 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注解

    @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";
        }    
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    @ControllerAdvice属性

    @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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    可以看到,有几个属性,其中:

    1.basePackages

    指定一个或多个包,这些包及其子包下的所有 Controller 都被该 @ControllerAdvice 管理。

    @RestControllerAdvice(basePackages={"com.example.spring"})
    @Slf4j
    public class ExceptionHandlerAdvice {    
        @ExceptionHandler(Exception.class)    
        public String handleException(Exception e) {    
            return "error";
        }   
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.basePackageClasses

    是 basePackages 的一种变形,指定一个或多个 Controller 类,这些类所属的包及其子包下的所有 Controller 都被该 @ControllerAdvice 管理。

    @RestControllerAdvice(basePackageClasses={TestController.class})
    @Slf4j
    public class ExceptionHandlerAdvice {
    	@ExceptionHandler(Exception.class)    
        public String handleException(Exception e) {    
            return "error";
        } 
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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";
        } 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    参考:https://blog.csdn.net/user2025/article/details/105458842
    https://blog.csdn.net/qq_43581790/article/details/123871439

  • 相关阅读:
    传感器的基本特性
    女生做软件测试会不会成为一个趋势?
    CRGDFPASSC,CAS号:166184-23-2
    第一天商城项目
    qt pro如何增加自定义值为的字符串的宏
    微信小程序案例3-1 比较数字
    基于北方苍鹰优化算法的函数寻优算法
    android 解决AVC问题
    Codeforces 1605C. Dominant Character
    Java版企业电子招标采购系统源码Spring Cloud + Spring Boot +二次开发+ MybatisPlus + Redis
  • 原文地址:https://blog.csdn.net/qq_43745578/article/details/127990603