Spring自定义注解
目录
一、@interface 关键字
二、元注解
三、简单实现
四、使用切面执行自定义注解逻辑
1) 首先将刚才的注解修改成放在方法上的:
2) 定义一个切面类:
3)将注解放入到接口方法中测试:
五、切点表达式
一、@interface 关键字
我们想定义一个自己的注解 需要使用 @interface 关键字来定义。 如定义一个叫 MyAnnotation 的注解:
public @interface MyAnnotation { }
二、元注解
光加上 @interface 关键字 还不够,我们还需要了解5大元注解
@Retention @Target @Documented @Inherited(JDK8 引入) @Repeatable(JDK8 引入)
1) @Retention 指定注解的生命周期
@Retention(RetentionPolicy.SOURCE) 其中Retention是一个枚举类:
RetentionPolicy.SOURCE : 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃(.java文件) RetentionPolicy.CLASS :注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期(.class文件) RetentionPolicy.RUNTIME: 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在(内存中的字节码) 2) @Target指定注解可以修饰的元素类型
@Target(ElementType.Field) ElementType.ANNOTATION_TYPE - 标记的注解可以应用于注解类型。 ElementType.CONSTRUCTOR - 标记的注解可以应用于构造函数。 ElementType.FIELD - 标记的注解可以应用于字段或属性。 ElementType.LOCAL_VARIABLE - 标记的注解可以应用于局部变量。 ElementType.METHOD - 标记的注解可以应用于方法。 ElementType.PACKAGE - 标记的注解可以应用于包声明。 ElementType.PARAMETER - 标记的注解可以应用于方法的参数。 ElementType.TYPE - 标记的注解可以应用于类的任何元素。 3)@Documented 指定注解会被JavaDoc工具提取成文档。默认情况下,JavaDoc是不包括文档的
4)@Inherited 表示该注解会被子类继承,注意,仅针对类,成员属性、方法并不受此注释的影响。
5)@Repeatable 表示注解可以重复使用,为了解决同一个注解不能重复在同一类/方法/属性上使用的问题。
其中最常用的就是 @Retention 跟 @Target。
三、简单实现
例如实现一个简单,在标记注解的地方打印一句日志。 定义一个 MyAnnotation 注解,并且定义一个属性 message 默认值是 ”aaa“。先将该注解加到字段上,看能不能获取到。
//注解用于字段上 @Target(ElementType.FIELD) //运行时使用 @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String message() default "aaa"; }定义一个Student类用于测试:
@Data public class Student { @JSONField(ordinal = 0)@MyAnnotation(message = "AAAAAAAAA")public String name;@MyAnnotation(message = "AAAAAAAAA")public Integer score; }
在字段上标注该注解,然后编写一个main方法获取该注解的属性:
public static void main(String[] args) {Class<?> studentClass = Student.class;//获取直接Field[] fields = studentClass.getDeclaredFields();//获取所有的类成员变量字段for (Field field : fields) {String fieldName = field.getName(); //获取该类成员变量的名字System.out.println("成员变量名是:" + fieldName);Annotation[] annotations = field.getAnnotations(); //获取该类成员变量上所有声明周期是运行时的注解for (Annotation annotation : annotations) {//annotationsClass<? extends Annotation> annotationType = annotation.annotationType();String annotationName = annotationType.getSimpleName();//注解的简短名称System.out.println(" 使用的注解是:" + annotationName);//判断该注解是不是 MyAnnotation 注解,是的话打印其 id 和 describe 属性if (annotationType.equals(MyAnnotation.class)) {MyAnnotation myAnnotation = field.getAnnotation(MyAnnotation.class);String message = myAnnotation.message();System.out.println(" MyAnnotation注解中的message是:" + message);}}System.out.println();}
}
执行后打印的内容:
以上就是一个注解的简单实现。
四、使用切面执行自定义注解逻辑
在开发中一般加上注解之后会自动执行一些逻辑,大部分实现的原理是使用AOP切面来实现注解的逻辑的。
1) 首先将刚才的注解修改成放在方法上的:
//注解用于方法
@Target(ElementType.METHOD)
//运行时使用
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String message() default "aaa";
}
2) 定义一个切面类:
@Component @Aspect @Slf4j public class MyAnnotationAspect {
/** 这是一个切入点* */ @Pointcut("@annotation(com.demo.aaa.annotation.MyAnnotation)") public void cutMethod(){} /*** 切点之前*/ @Before("cutMethod()") public void before(JoinPoint joinPoint) throws Throwable {log.info("============ before =========="); }/*** 切点之后*/ @After("cutMethod()") public void after() throws Throwable {log.info("============ after =========="); }/*** 切点返回内容后*/ @AfterReturning("cutMethod()") public void afterReturning() throws Throwable {log.info("============ afterReturning =========="); }/*** 切点抛出异常后*/ @AfterThrowing("cutMethod()") public void afterThrowing() throws Throwable {log.info("============ afterThrowing =========="); }
@Around("cutMethod() && @annotation(myAnnotation)") public Object around(ProceedingJoinPoint point, MyAnnotation myAnnotation) throws Throwable {log.info("============ around1 ==========");Object obj= point.proceed(point.getArgs());log.info("============ around2 ==========");return obj; }}
在使用aop之前需要先引入一个依赖:
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.13</version></dependency>
简单说一下各个注解代表什么含义:
@Aspect:作用是把当前类标识为一个切面供容器读取 ,也就是加上这个注解,spring才知道你这是一个切面类,用于处理切点的逻辑的。 @Pointcut:切入点,@Pointcut切点表达式非常丰富,可以将 方法(method)、类(class)、接口(interface)、包(package) 等作为切入点,非常灵活,常用的有@annotation、@within、execution等方式,上面的示例使用的是@annotation方式,意思就是说被Spring扫描到方法上带有@annotation中的注解 就会执行切面通知。 @Before:该注解标注的方法在业务模块代码执行之前执行,其不能阻止业务模块的执行,除非抛出异常; @AfterReturning:该注解标注的方法在业务模块代码执行之后执行; @AfterThrowing:该注解标注的方法在业务模块抛出指定异常后执行; @After:该注解标注的方法在所有的 Advice 执行完成后执行,无论业务模块是否抛出异常,类似于 finally 的作用; @Around:该注解功能最为强大,其所标注的方法用于编写包裹业务模块执行的代码,通知的第一个参数必须是 ProceedingJoinPoint 类型。在通知体内,调用 ProceedingJoinPoint 的 proceed () 方法使得连接点方法执行如果不调用 proceed () 方法,连接点方法则不会执行。无论是调用前逻辑还是调用后逻辑,都可以在该方法中编写,甚至其可以根据一定的条件而阻断业务模块的调用; 如果切面中使用了@Around 注解,如果不调用 ProceedingJoinPoint 的 proceed () 方法的话,那么 @Before 和 @After 直接标注的方法也不会被触发。@Around 注解标注的方法,在 ProceedingJoinPoint 的 proceed () 方法 前的逻辑是比@Before的逻辑还要靠前, 在proceed () 方法之后的逻辑比 @After 的逻辑还要靠后。 Joint Point:JointPoint是程序运行过程中可识别的点,这个点可以用来作为AOP切入点。JointPoint对象则包含了和切入相关的很多信息。比如切入点的对象,方法,属性等。我们可以通过反射的方式获取这些点的状态和信息,用于追踪tracing和记录logging应用信息。
3)将注解放入到接口方法中测试:
@GetMapping("/aaa")
@MyAnnotation(message = "成功拉!!!!!!!!!!!!")
public void test() {System.out.println("执行代码逻辑");
}
调用接口之后打印
上面就是自定义注解最简单的示例。
五、切点表达式
我们定义切点除了使用 @Pointcut() 之外,我们还有丰富的切点表达式可以定义切点。
1)切点表达式简介 2)通配符合与逻辑运算符
@AspectJ 支持三种通配符:
逻辑运算符: 切点表达式由切点函数组成,切点函数之间还可以进行逻辑运算,组成复合切点。
3)切点表达式: 1.arg() :匹配切入点方法的参数类型,匹配的上才是切点。语法:args(param-pattern) param-pattern:参数类型的全路径。注意:要先匹配到某些类,不然会报错,也就是不能单独用示例:
@Pointcut("args(java.lang.String)") //这样就是错的,不能单独使用要匹配到某些类
@Pointcut("within(com.example.demo.service.impl.UserServiceImpl) && args(java.lang.String,java.lang.String)") //要像这样使用 within 先匹配到某个具体的类,在使用args匹配到某个类型参数的方法 2.@args:匹配切入点方法上的参数的类上,参数的类必须要有指定的注解 语法:@args(annotation-type) annotation-type:注解类型的全路径 注意:也不能单独使用,必须先指定到类,而且匹配参数个数至少有一个且为第一个参数的类含有该注解才能匹配的上 示例:
@Pointcut("within(com.demo.RedisTest) && @args(com.demo.aaa.annotation.MyAnnotation)") 3.within:匹配切入点的指定类的任意方法,不能匹配接口。 语法:within(declaring-type) 参数为全路径的类名(可使用通配符),表示匹配当前表达式的所有类都将被当前方法环绕 注意: 这个是指定到具体的类 示例:
//within表达式的粒度为类,其参数为全路径的类名(可使用通配符),表示匹配当前表达式的所有类都将被当前方法环绕。如下是within表达式的语法: @Pointcut(within(declaring-type-pattern))
//within表达式只能指定到类级别,如下示例表示匹配com.spring.service.BusinessObject中的所有方法: @Pointcut(within(com.spring.service.BusinessObject)) //within表达式路径和类名都可以使用通配符进行匹配,比如如下表达式将匹配com.spring.service包下的所有类,不包括子包中的类: @Pointcut(within(com.spring.service.*))
//如下表达式表示匹配com.spring.service包及子包下的所有类: @Pointcut(within(com.spring.service..*)) 4.@within:表示匹配带有指定注解的类。 语法:@within(annotation-type) 注解的全类名 注意:这个是指定到带有某个注解的类 示例:
//如下所示示例表示匹配使用com.spring.annotation.BusinessAspect注解标注的类:
@within(com.spring.annotation.BusinessAspect) 5.@annotation() :匹配带有指定注解的连接点 语法:@annotation(annotation-type) annotation-type:注解类型的全路径 示例:
@Pointcut("@annotation(com.test.annotations.LogAuto)") 6.execution() 用于匹配是连接点的执行方法,Spring 切面粒度最小是达到方法级别,而 execution 表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的配置,所以是使用最广泛的。 用法:
modifiers-pattern:方法的可见性修饰符,如 public,protected,private; ret-type-pattern:方法的返回值类型,如 int,void 等; declaring-type-pattern:方法所在类的全路径名,如 com.spring.Aspect; name-pattern:方法名,如 getOrderDetail(); param-pattern:方法的参数类型,如 java.lang.String; throws-pattern:方法抛出的异常类型,如 java.lang.Exception; 示例:
modifiers-pattern:方法的可见性修饰符,如 public,protected,private; ret-type-pattern:方法的返回值类型,如 int,void 等; declaring-type-pattern:方法所在类的全路径名,如 com.spring.Aspect; name-pattern:方法名,如 getOrderDetail(); param-pattern:方法的参数类型,如 java.lang.String; throws-pattern:方法抛出的异常类型,如 java.lang.Exception; 示例:
// 匹配目标类的所有 public 方法,第一个 * 代表返回类型,第二个 * 代表方法名,..代表方法的参数 execution(public * *(..))
// 匹配目标类所有以 User 为后缀的方法。第一个 * 代表返回类型,User 代表以 User 为后缀的方法 execution( *User(..))
// 匹配 User 类里的所有方法 execution(* com.test.demo.User.*(..))
// 匹配 User 类及其子类的所有方法 execution(* com.test.demo.User+.*(..)) :
// 匹配 com.test 包下的所有类的所有方法 execution(* com.test..(..))
// 匹配 com.test 包下及其子孙包下所有类的所有方法 execution(* com.test...(..)) :
// 匹配 getOrderDetail 方法,且第一个参数类型是 Long,第二个参数类型是 String execution(* getOrderDetail(Long, String))
六、切面中获取各个参数 示例:
@Around(value = "@annotation(basisLogAnnotation)") public Object demoAop(ProceedingJoinPoint proceedingJoinPoint, final BasisLogAnnotation basisLogAnnotation) throws Throwable {
logger.debug("执行前:");Object object = proceedingJoinPoint.proceed(); //执行连接点方法,object:方法返回值logger.debug("执行后:");// 类名String className = proceedingJoinPoint.getTarget().getClass().getName();//方法名String methodName = proceedingJoinPoint.getSignature().getName();//参数(我这里是对象,具体根据个人的参数类型来强转)BasisUser basisUser = (BasisUser)proceedingJoinPoint.getArgs()[0];return object; }
原文链接:Java 实现自定义注解_java 自定义注解-CSDN博客
相关文章:
Spring自定义注解
目录 一、interface 关键字 二、元注解 三、简单实现 四、使用切面执行自定义注解逻辑 1) 首先将刚才的注解修改成放在方法上的: 2) 定义一个切面类: 3)将注解放入到接口方法中测试: 五、切点表达式 一、interface 关键字 …...
微信小程序:wx.login或调用uni.login时报错the code is a mock one
微信小程序,调用wx.login或调用uni.login方法,返回the code is a mock one 原因与解决 原因:没有关联真实的 appid,解决办法:绑定真实的微信小程序的appid...
URL的执行流程
基本概念: URL(统一资源定位符,Uniform Resource Locator)的执行流程是指当你在浏览器中输入一个URL并按下回车键时,从输入URL到最终在浏览器中显示网页的完整过程。 1.解析协议 URL 以协议开头,如 http…...
双指针算法专题(2)
找往期文章包括但不限于本期文章中不懂的知识点: 个人主页:我要学编程(ಥ_ಥ)-CSDN博客 所属专栏: 优选算法专题 想要了解双指针算法的介绍,可以去看下面的博客:双指针算法的介绍 目录 611.有效三角形的个数 LCR 1…...
加密与安全_优雅存储用户密码的最佳实践
文章目录 Pre概述最佳实践避免使用MD5、SHA1等快速哈希算法加盐哈希 (不推荐)使用BCrypt、Argon2等慢哈希算法 (推荐)BCrypt Code1. 自动生成和嵌入盐2. 哈希结果的格式3. 代价因子 BCrypt特点 防止暴力破解1. 登录失败锁定2. 双因素认证(2FA…...
【多线程】深入剖析线程池的应用
💐个人主页:初晴~ 📚相关专栏:多线程 / javaEE初阶 还记得我们一开始引入线程的概念,就是因为进程太“重”了,频繁创建销毁进程的开销是非常大的。而随着计算机的发展,业务上对性能的要求越来越…...
『功能项目』切换职业面板【48】
我们打开上一篇47技能冷却蒙版的项目, 本章要做的事情是切换职业UI面板的功能 首先双击打开Canvas预制体在左上主角面板信息中新建一个button按钮 重命名(父物体是按钮Button,子物体Image即可) 创建一个Image 设计一下布局 复制三…...
【EasyExcel】@ColumnWidth(value = 20) EasyExcel设置列宽不生效
经过测试发现,只有XLS,ColumnWidth注解才会生效,选择CSV和XLSX都不会生效 //对应的导出实体类 EasyExcel.write(outputStream, Result.class)//excel文件类型,包括CSV、XLS、XLSX.excelType(ExcelTypeEnum.XLS)...
CPU 和 GPU:为什么GPU更适合深度学习?
目录 什么是 CPU ? 什么是 GPU ? GPU vs CPU 差异性对比分析 GPU 是如何工作的 ? GPU 与 CPU 是如何协同工作的 ? GPU vs CPU 类型解析 GPU 应用于深度学习 什么是 CPU ? CPU(中央处理器)…...
【机器学习】:解锁数据背后的智慧宝藏——深度探索与未来展望
欢迎来到 破晓的历程的 博客 ⛺️不负时光,不负己✈️ 文章目录 引言一、深入机器学习的内在机制二、最新进展与趋势三、对未来社会的深远影响结语 引言 在上一篇博客中,我们初步探讨了机器学习如何成为解锁数据背后智慧的关键工具。现在,让…...
【Kubernetes】常见面试题汇总(十八)
目录 55.简述 Kubernetes 共享存储的作用? 56.简述 Kubernetes 数据持久化的方式有哪些? 57.简述 Kubernetes PV 和 PVC ? 58.简述 Kubernetes PV 生命周期内的阶段? 55.简述 Kubernetes 共享存储的作用? Kubernet…...
无限边界:现代整合安全如何保护云
尽管云计算和远程工作得到广泛采用,零信任网络也稳步推广,但边界远未消失。相反,它已被重新定义。就像数学分形的边界一样,现代网络边界现在无限延伸到任何地方。 不幸的是,传统工具在现代无限边界中效果不佳。现代边…...
HTML贪吃蛇游戏
文章目录 贪吃蛇游戏 运行效果代码 贪吃蛇游戏 贪吃蛇是一款经典的休闲益智游戏。本文将通过HTML5和JavaScript详细解析如何实现一个简易版的贪吃蛇游戏。游戏的主要逻辑包括蛇的移动、碰撞检测、食物生成等功能。以下是游戏的完整代码及注释解析。(纯属好玩&#…...
HTML 揭秘:HTML 编码快速入门
HTML 揭秘:HTML 编码快速入门 一 . 前端知识介绍二 . HTML 介绍三 . HTML 快速入门四 . HTML 编辑器 - VSCode4.1 插件安装4.2 修改主题配色4.3 修改快捷键4.4 设置自动保存4.5 创建 HTML 文件4.5 书写 HTML 代码4.6 常见快捷键 五 . 基础标签5.1 字体标签5.1.1 col…...
Ubuntu22.04系统安装opencv步骤简述及问题解决方法
前言 opencv是一个功能强大、开源且跨平台的计算机视觉库,适用于多种编程语言和操作系统,能够帮助开发者构建各种视觉项目。其模块众多,提供了诸多功能,能够进行图像处理、视频处理等等。比如:Highgui模块提供图像用户…...
移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——13.mapset
1. 关联式容器 在初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、 forward_list(C11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面 存储的是元素本身。那什么是关…...
【webpack4系列】webpack基础用法(二)
文章目录 entryoutputloaderpluginmode前端构建基础配置关联HTML插件html-webpack-plugin构建 CSS 解析 ES6和React JSX解析 ES6解析 React JSX 解析CSS、Less和Sass解析CSS解析Less解析sass 解析图片和字体资源解析:解析图片资源解析:解析字体资源解析&…...
Python Pyvis库创建交互式网络图 高级功能详解
文章目录 动态网络图图布局调整扩展到大规模网络动态网络图 Pyvis支持创建动态网络图,通过时间轴展示网络图的演化过程。 需要使用set_options函数,参数必须为json格式。动态网络图支持添加点和边。 下面是一个简单的动态网络图示例: # 动态网络图示例 from pyvis.networ…...
Linux服务器上安装git lfs命令
有时候,需要批量下载数据集时要用到git lfs命令 首先,使用pip install git-lfs安装,会发现使用时仍然提示:git: lfs is not a git command. See git --help. 这就意味着安装不成功。 因此,需要通过如下途径手动安装&a…...
S100A9:鸡支原体感染中的免疫调控“双面间谍”【AbMole】
在生物学研究的广阔天地里,总有一些分子扮演着令人意想不到的角色。今天,我们要探索的主角是S100A9蛋白,一种在鸡支原体感染过程中展现出惊人双重功能的分子。这项来自华中农业大学动物科技学院与兽医学院的最新研究成果,揭示了S1…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
