@ControllerAdvice 的介绍及三种用法

@ControllerAdvice 的介绍及三种用法

浅析@ControllerAdvice

首先,ControllerAdvice本质上是一个Component,因此也会被当成组建扫描,一视同仁,扫扫扫。

ControllerAdvice.class

然后,我们来看一下此类的注释:

这个类是为那些声明了(@ExceptionHandler@InitBinder 或 @ModelAttribute注解修饰的)方法的类而提供的专业化的@Component , 以供多个 Controller类所共享。

说白了,就是aop思想的一种实现,你告诉我需要拦截规则,我帮你把他们拦下来,具体你想做更细致的拦截筛选和拦截之后的处理,你自己通过@ExceptionHandler@InitBinder 或 @ModelAttribute这三个注解以及被其注解的方法来自定义。

在这里插入图片描述

初定义拦截规则:

ControllerAdvice 提供了多种指定Advice规则的定义方式,默认什么都不写,则是Advice所有Controller,当然你也可以通过下列的方式指定规则
比如对于 String[] value() default {} , 写成@ControllerAdvice("org.my.pkg") 或者 @ControllerAdvice(basePackages="org.my.pkg"), 则匹配org.my.pkg包及其子包下的所有Controller,当然也可以用数组的形式指定,如:@ControllerAdvice(basePackages={"org.my.pkg", "org.my.other.pkg"}), 也可以通过指定注解来匹配,比如我自定了一个 @CustomAnnotation 注解,我想匹配所有被这个注解修饰的 Controller, 可以这么写:@ControllerAdvice(annotations={CustomAnnotation.class})

还有很多用法,这里就不全部罗列了。

@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.处理全局异常

@ControllerAdvice 配合 @ExceptionHandler 实现全局异常处理

在这里插入图片描述

用于在特定的处理器类、方法中处理异常的注解

在这里插入图片描述

接收Throwable类作为参数,我们知道Throwable是所有异常的父类,所以说,可以自行指定所有异常

比如在方法上加:@ExceptionHandler(IllegalArgumentException.class),则表明此方法处理

IllegalArgumentException 类型的异常,如果参数为空,将默认为方法参数列表中列出的任何异常(方法抛出什么异常都接得住)。

下面的例子:处理所有IllegalArgumentException异常,域中加入错误信息errorMessage 并返回错误页面error

@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(IllegalArgumentException.class)public ModelAndView handleException(IllegalArgumentException e){ModelAndView modelAndView = new ModelAndView("error");modelAndView.addObject("errorMessage", "参数不符合规范!");return modelAndView;}
}

2.预设全局数据

@ControllerAdvice 配合 @ModelAttribute 预设全局数据

我们先来看看 ModelAttribute注解类的源码

/*** Annotation that binds a method parameter or method return value* to a named model attribute, exposed to a web view. Supported* for controller classes with {@link RequestMapping @RequestMapping}* methods.* 此注解用于绑定一个方法参数或者返回值到一个被命名的model属性中,暴露给web视图。支持在* 在Controller类中注有@RequestMapping的方法使用(这里有点拗口,不过结合下面的使用介绍* 你就会明白的)*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ModelAttribute {@AliasFor("name")String value() default "";@AliasFor("value")String name() default "";boolean binding() default true;}

实际上这个注解的作用就是,允许你往 Model 中注入全局属性(可以供所有Controller中注有@Request Mapping的方法使用),value 和 name 用于指定 属性的 key ,binding 表示是否绑定,默认为 true

具体使用方法如下:

  • 全局参数绑定

    • 方式一:
    @ControllerAdvice
    public class MyGlobalHandler {@ModelAttributepublic void presetParam(Model model){model.addAttribute("globalAttr","this is a global attribute");}
    }
    

    这种方式比较灵活,需要什么自己加就行了,加多少属性自己控制

    • 方式二:
    @ControllerAdvice
    public class MyGlobalHandler {@ModelAttribute()public Map<String, String> presetParam(){Map<String, String> map = new HashMap<String, String>();map.put("key1", "value1");map.put("key2", "value2");map.put("key3", "value3");return map;}}
    

    这种方式对于加单个属性比较方便。默认会把返回值(如上面的map)作为属性的value,而对于key有两种指定方式:

    1. 当 @ModelAttribute() 不传任何参数的时候,默认会把返回值的字符串值作为key,如上例的 key 则是 ”map"(值得注意的是,不支持字符串的返回值作为key)。
    2. 当 @ModelAttribute("myMap") 传参数的时候,则以参数值作为key,这里 key 则是 ”myMap“。
  • 全局参数使用

    @RestController
    public class AdviceController {@GetMapping("methodOne")public String methodOne(Model model){ Map<String, Object> modelMap = model.asMap();return (String)modelMap.get("globalAttr");}@GetMapping("methodTwo")public String methodTwo(@ModelAttribute("globalAttr") String globalAttr){return globalAttr;}@GetMapping("methodThree")public String methodThree(ModelMap modelMap) {return (String) modelMap.get("globalAttr");}}
    

    这三种方式大同小异,其实都是都是从Model 中存储属性的 Map里取数据。

3.请求参数预处理

@ControllerAdvice 配合 @InitBinder 实现对请求参数的预处理

再次之前我们先来了解一下 @IniiBinder,先看一下源码,我会提取一些重要的注释进行浅析

/*** Annotation that identifies methods which initialize the* {@link org.springframework.web.bind.WebDataBinder} which* will be used for populating command and form object arguments* of annotated handler methods.* 粗略翻译:此注解用于标记那些 (初始化[用于组装命令和表单对象参数的]WebDataBinder)的方法。* 原谅我的英语水平,翻译起来太拗口了,从句太多就用‘()、[]’分割一下便于阅读** Init-binder methods must not have a return value; they are usually* declared as {@code void}.* 粗略翻译:初始化绑定的方法禁止有返回值,他们通常声明为 'void'** <p>Typical arguments are {@link org.springframework.web.bind.WebDataBinder}* in combination with {@link org.springframework.web.context.request.WebRequest}* or {@link java.util.Locale}, allowing to register context-specific editors.* 粗略翻译:典型的参数是`WebDataBinder`,结合`WebRequest`或`Locale`使用,允许注册特定于上下文的编辑 * 器。* * 总结如下:*  1. @InitBinder 标识的方法的参数通常是 WebDataBinder。*  2. @InitBinder 标识的方法,可以对 WebDataBinder 进行初始化。WebDataBinder 是 DataBinder 的一* 		           个子类,用于完成由表单字段到 JavaBean 属性的绑定。*  3. @InitBinder 标识的方法不能有返回值,必须声明为void。*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InitBinder {/*** The names of command/form attributes and/or request parameters* that this init-binder method is supposed to apply to.* <p>Default is to apply to all command/form attributes and all request parameters* processed by the annotated handler class. Specifying model attribute names or* request parameter names here restricts the init-binder method to those specific* attributes/parameters, with different init-binder methods typically applying to* different groups of attributes or parameters.* 粗略翻译:此init-binder方法应该应用于的命令/表单属性和/或请求参数的名称。默认是应用于所有命	   		* 令/表单属性和所有由带注释的处理类处理的请求参数。这里指定模型属性名或请求参数名将init-binder		 * 方法限制为那些特定的属性/参数,不同的init-binder方法通常应用于不同的属性或参数组。* 我至己都理解不太理解这说的是啥呀,我们还是看例子吧*/String[] value() default {};
}

我们来看看具体用途,其实这些用途在 Controller里也可以定义,但是作用范围就只限当前Controller,因此下面的例子我们将结合 ControllerAdvice 作全局处理。

  • 参数处理

    @ControllerAdvice
    public class MyGlobalHandler {@InitBinderpublic void processParam(WebDataBinder dataBinder){/** 创建一个字符串微调编辑器* 参数{boolean emptyAsNull}: 是否把空字符串("")视为 null*/StringTrimmerEditor trimmerEditor = new StringTrimmerEditor(true);/** 注册自定义编辑器* 接受两个参数{Class<?> requiredType, PropertyEditor propertyEditor}* requiredType:所需处理的类型* propertyEditor:属性编辑器,StringTrimmerEditor就是 propertyEditor的一个子类*/dataBinder.registerCustomEditor(String.class, trimmerEditor);//同上,这里就不再一步一步讲解了binder.registerCustomEditor(Date.class,new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), false));}
    }
    

    这样之后呢,就可以实现全局的实现对 Controller 中RequestMapping标识的方法中的所有 String 和Date类型的参数都会被作相应的处理。

    Controller:

    @RestController
    public class BinderTestController {@GetMapping("processParam")public Map<String, Object> test(String str, Date date) throws Exception {Map<String, Object> map = new HashMap<String, Object>();map.put("str", str);map.put("data", date);return  map;}
    }
    

    测试结果:

在这里插入图片描述
我们可以看出,str 和 date 这两个参数在进入 Controller 的test的方法之前已经被处理了,str 被去掉了两边的空格(%20 在Http url 中是空格的意思),String类型的 1997-1-10被转换成了Date类型。

  • 参数绑定

    参数绑定可以解决特定问题,那么我们先来看看我们面临的问题

    class Person {private String name;private Integer age;// omitted getters and setters.
    }class Book {private String name;private Double price;// omitted getters and setters.
    }@RestController
    public class BinderTestController {@PostMapping("bindParam")public void test(Person person, Book book) throws Exception {System.out.println(person);System.out.println(book);}
    }
    

    我们会发现 Person类和 Book 类都有 name属性,那么这个时候就会出先问题,它可没有那么只能区分哪个name是哪个类的。因此 @InitBinder就派上用场了:

    @ControllerAdvice
    public class MyGlobalHandler {/** @InitBinder("person") 对应找到@RequstMapping标识的方法参数中* 找参数名为person的参数。* 在进行参数绑定的时候,以‘p.’开头的都绑定到名为person的参数中。*/@InitBinder("person")public void BindPerson(WebDataBinder dataBinder){dataBinder.setFieldDefaultPrefix("p.");}@InitBinder("book")public void BindBook(WebDataBinder dataBinder){dataBinder.setFieldDefaultPrefix("b.");}
    }
    

    因此,传入的同名信息就能对应绑定到相应的实体类中:

    p.name -> Person.name b.name -> Book.name

    还有一点注意的是如果 @InitBinder("value") 中的 value 值和 Controller 中 @RequestMapping() 标识的方法的参数名不匹配,则就会产生绑定失败的后果,如:

    @InitBinder(“p”)、@InitBinder(“b”)

    public void test(Person person, Book book)

    上述情况就会出现绑定失败,有两种解决办法

    第一中:统一名称,要么全叫p,要么全叫person,只要相同就行。

    第二种:方法参数加 @ModelAttribute,有点类似@RequestParam

    @InitBinder(“p”)、@InitBinder(“b”)

    public void test(@ModelAttribute(“p”) Person person, @ModelAttribute(“b”) Book book)

如果有不足之处,欢迎在评论区评论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/472830.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

代码随想录算法训练营29期|day53 任务以及具体安排

第九章 动态规划part10 121. 买卖股票的最佳时机 // 解法1 class Solution {public int maxProfit(int[] prices) {if (prices null || prices.length 0) return 0;int length prices.length;// dp[i][0]代表第i天持有股票的最大收益// dp[i][1]代表第i天不持有股票的最大收…

人工智能学习与实训笔记(二):神经网络之图像分类问题

目录 四、图像分类问题 4.1 尝试使用全连接神经网络 4.2 引入卷积神经网络 4.3 分类函数Softmax 4.4 交叉熵损失函数 4.5 学习率优化算法 4.6 图像预处理算法 4.6.1 随机改变亮暗、对比度和颜色等 4.6.2 随机填充 4.6.3 随机裁剪 4.6.4 随机缩放 4.6.5 随机翻转 4.…

【2024】如何订阅Netflix奈飞?Netflix奈飞购买教程

【2024】如何订阅Netflix奈飞&#xff1f;Netflix奈飞购买教程 Netflix奈飞作为全球领先的在线流媒体平台之一&#xff0c;拥有丰富的影视资源和独家内容&#xff0c;成为了人们追剧的热门选择。本文将为您介绍如何订阅Netflix奈飞&#xff0c;并提供详细的购买教程&#xff0…

人工智能学习与实训笔记(五):神经网络之推荐系统处理

目录 ​​​​​​​七、智能推荐系统处理 7.1 常用的推荐系统算法 7.2 如何实现推荐​​​​​​​ 7.3 基于飞桨实现的电影推荐模型 7.3.1 电影数据类型 7.3.2 数据处理 7.3.4 数据读取器 7.3.4 网络构建 7.3.4.1用户特征提取 7.3.4.2 电影特征提取 7.3.4.3 相似度…

红队ATKCK|红日靶场Write-Up(附下载链接)

网络拓扑图 下载地址 在线下载&#xff1a; http://vulnstack.qiyuanxuetang.net/vuln/detail/2/ 百度网盘 链接&#xff1a;https://pan.baidu.com/s/1nlAZAuvni3EefAy1SGiA-Q?pwdh1e5 提取码&#xff1a;h1e5 环境搭建 通过上述图片&#xff0c;web服务器vm1既能用于外…

Flume(二)【Flume 进阶使用】

前言 学数仓的时候发现 flume 落了一点&#xff0c;赶紧补齐。 1、Flume 事务 Source 在往 Channel 发送数据之前会开启一个 Put 事务&#xff1a; doPut&#xff1a;将批量数据写入临时缓冲区 putList&#xff08;当 source 中的数据达到 batchsize 或者 超过特定的时间就会…

[ai笔记9] openAI Sora技术文档引用文献汇总

欢迎来到文思源想的ai空间&#xff0c;这是技术老兵重学ai以及成长思考的第9篇分享&#xff01; 这篇笔记承接上一篇技术文档的学习&#xff0c;主要是为了做一个记录&#xff0c;记录下openai sora技术介绍文档提到的一些论文&#xff0c;再此特地记录一下&#xff01; 1 原文…

安全基础~通用漏洞5

文章目录 知识补充CSRFSSRFxss与csrf结合创建管理员账号 知识补充 NAT&#xff1a;网络地址转换&#xff0c;可以将IP数据报文头中的IP地址转换为另一个IP地址&#xff0c;并通过转换端口号达到地址重用的目的。即通过将一个外部IP地址和端口映射更大的内部IP地址集来转换IP地…

真假难辨 - Sora(OpenAI)/世界模拟器的技术报告

目录 引言技术报告汉译版英文原版 引言 Sora是OpenAI在2024年2月15日发布的世界模拟器&#xff0c;功能是通过文本可以生成一分钟的高保真视频。由于较高的视频质量&#xff0c;引起了巨大关注。下面是三个示例&#xff0c;在示例之后给出了其技术报告&#xff1a; tokyo-wal…

C++,stl,函数对象,谓词,内建函数对象

目录 1.函数对象使用 2.谓词 1.一元谓词 2.二元谓词 3.内建函数对象 1.算术仿函数 2.关系仿函数 3.逻辑仿函数 1.函数对象使用 #include<bits/stdc.h> using namespace std;class add { public:int operator()(int v1,int v2){return v1 v2;} };class print { p…

WebStorm | 如何修改webstorm中新建html文件默认生成模板中title的初始值

在近期的JS的学习中&#xff0c;使用webstorm&#xff0c;总是要先新建一个html文件&#xff0c;然后再到里面书写<script>标签&#xff0c;真是麻烦&#xff0c;而且标题也是默认的title&#xff0c;想改成文件名还总是需要手动去改 经过小小的研究&#xff0c;找到了修…

智慧公厕管理软件

随着城市化的不断推进&#xff0c;城市公共设施逐渐完善&#xff0c;其中智慧公厕的建设也在不断提速。智慧公厕作为城市基础设施的重要组成部分&#xff0c;对城市卫生水平提升有着不可忽视的作用。而智慧公厕管理软件更是智慧公厕管理的基础&#xff0c;是公共厕所智慧化管理…