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博客

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

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

相关文章

ROADM(可重构光分插复用器)-介绍

1. 引用 https://zhuanlan.zhihu.com/p/163369296 https://zhuanlan.zhihu.com/p/521352954 https://zhuanlan.zhihu.com/p/91103069 https://zhuanlan.zhihu.com/p/50610236 术语&#xff1a; 英文缩写描述灰光模块彩光模块CWDM&#xff1a;Coarse Wave-Length Division …

嵌入式学习--linux系统提供的显示接口”framebuffer“

1.利用framebuffer绘制图像 宏定义 1.#define RGB888_FMT 32 2.#define RGB565_FMT 16 3.#include"utf.h" 函数接口主体 void draw_bmp(int x, int y, char *picname, int w, int h) {int fd open(picname, O_RDONLY);if (-1 fd){perror("fail open bmp&quo…

IT前端好用的工具集

在线抠图网站 https://www.remove.bg/ 将iconfont转成css显示 https://transfonter.org/ 免费的在线图片压缩 https://tinypng.com/ JSON在线格式化工具 https://www.sojson.com/ 国内人工智能kimi.moonshot工具 https://kimi.moonshot.cn/chat/crft7a6sdv14grouufs0 自动…

python之pyecharts制作可视化数据大屏

文章目录 前言一、安装 Pyecharts二、创建 Pyecharts 图表三、设计大屏布局四、实时数据更新五、部署和展示总结前言 使用 Pyecharts 制作可视化数据大屏是一个复杂但有趣的过程,因为 Pyecharts 本身是一个用于生成 Echarts 图表的 Python 库,而 Echarts 是由百度开发的一个…

CentOS系统内存突增简单分析

1. 实时监控内存使用 1.1 使用 free 命令 free 命令用于查看系统的整体内存使用情况: free -h输出示例: total used free shared buff/cache available Mem: 7.8G 2.1G 1.5G 112M 4.2G 5.4G Swap: …

Android生成Java AIDL

AIDL:Android Interface Definition Language AIDL是为了实现进程间通信而设计的Android接口语言 Android进程间通信有多种方式&#xff0c;Binder机制是其中最常见的一种 AIDL的本质就是基于对Binder的运用从而实现进程间通信 这篇博文从实战出发&#xff0c;用一个尽可能…

【深度学习】Pytorch基础

目录 梯度下降算法&#xff08;Gradient Descent&#xff09;代码实现 梯度下降算法&#xff08;Gradient Descent&#xff09; 梯度下降算法在机器学习中应用十分的广泛&#xff0c;不论是在线性回归还是Logistic回归中&#xff0c;它的主要目的是通过迭代找到目标函数的最小…

python-游戏自动化(三)(实战-豆腐女孩)

前提准备 特别注意&#xff1a; 本节教程所演示的模拟器分辨率设置为 720x1080&#xff08;手机版&#xff09;&#xff0c;电脑分辨率设置大720x1080并且没有设置放大。 今天的课程开始之前我们来回顾一下昨天所学的知识内容&#xff0c;因为今天要学的内容和昨天内容…

苹果能引领端侧AI大模型时代吗?

苹果能引领端侧AI时代吗&#xff1f; 这份完整版的大模型 AI 学习资料已经上传CSDN&#xff0c;朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】 北京时间9月10日凌晨&#xff0c;苹果正式发布了iPhone 16&#xff0c;这是苹果第一款真正意义上的 …

18. 如何在MyBatis中执行批量操作?批量操作的优点是什么?

批量操作通常指对数据库执行多条相同类型的SQL操作&#xff08;如插入、更新或删除&#xff09;&#xff0c;这在处理大数据量时非常常见。MyBatis 提供了多种方式来执行批量操作&#xff0c;常见的方式包括使用<foreach>标签和SqlSession的批量模式。 1. 使用<foreac…

网络安全(sql注入)

这里写目录标题 一. information_schema.tables 和 information_schema.schemata是information_schema数据库中的两张表1. information_schema.schemata2. information_schema.tables 二. 判断注入类型1. 判断数字型还是字符型注入2. 判断注入闭合是""还是 三. 判断表…

浅谈模型在信贷营销中的应用

浅谈模型在信贷营销中的应用 当前在信贷营销场景中,用户流量竞争愈加激烈,获客成本持续攀高,客户消费观念和消费信心趋向保守,传统的信贷营销方式效果逐渐乏力,借助数据挖掘技术对用户进行多元优化及精细化管理已经成为企业在经营发展中的普遍趋势。在此背景下,本文将围…

什么是API网关(API Gateway)?

1. 什么是API网关&#xff08;API Gateway&#xff09;&#xff1f; 在微服务体系结构中&#xff0c;客户端可能与多个前端服务进行交互。 API 网关位于客户端与服务之间。 它充当反向代理&#xff0c;将来自客户端的请求路由到服务。 它还可以执行各种横切任务&#xff0c;例…

什么是CPU、GPU、NPU?(包懂+会)

目录 举例子 CPU&#xff1a;主厨 GPU&#xff1a;大量的厨房助理 NPU&#xff1a;面包机 总结 讲理论 CPU&#xff08;中央处理器&#xff09; GPU&#xff08;图形处理单元&#xff09; NPU&#xff08;神经网络处理单元&#xff09; 对比分析 举例子 CPU&#xff…

【网络安全】-文件下载漏洞-pikachu

文件操作漏洞包括文件上传漏洞&#xff0c;文件包含漏洞&#xff0c;文件下载漏洞。 文章目录  前言 什么是文件下载漏洞&#xff1f; 1.常见形式&#xff1a; 常见链接形式&#xff1a; 常见参数&#xff1a; 2.利用方式&#xff1a; 3.举例&#xff1a;pikachu不安全的文件…

【Qt】按钮样式--按钮内部布局(调整按钮文本和图标放置在任意位置)

要求&#xff1a; 有一个按钮&#xff0c;要求按钮的右下角显示开关&#xff0c;点击切换开关状态 ps&#xff1a;注意&#xff0c;要求你添加完了之后&#xff0c;整个按钮的点击区域不变&#xff08;就是说&#xff0c;点击右下角的文本&#xff0c;也可以触发按钮的点击事件…

CentOS7 使用yum报错:[Errno 14] HTTP Error 404 - Not Found 正在尝试其它镜像。

CentOS7 使用yum报错&#xff1a;[Errno 14] HTTP Error 404 - Not Found 正在尝试其它镜像。 CentOS镜像下载、VM虚拟机下载 下载地址&#xff1a;www.macfxb.cn 一、问题描述 安装完CentOS7 后 使用yum报错 如下图 二、解决方案 1.查看自己的系统架构 我的是aarch64 uname …

python 学习一张图

python学习一张图&#xff0c;python的特点的是学的快&#xff0c;一段时间不用&#xff0c;忘记的也快&#xff0c;弄一张图及一些入门案例吧。 写一个简单的测试&#xff1a; #!/usr/bin/python # -*- coding: UTF-8 -*- import osdef add_num(a, b):return a bif __name__…

【STM32】BH1750光敏传感

1.BH1750介绍 BH1750是一个光敏传感&#xff0c;采用I2C协议&#xff0c;对于I2C的从机&#xff0c;都有自己的地址&#xff0c;用来主机选择和哪个从机通信&#xff0c;对于OLED来说&#xff0c;只有单片机通过I2C往OLED中写数据。而BH1750来说&#xff0c;有单片机往BH1750写…

DNAT和SNAT实践

NAT分SNAT和DNAT两种。从名字上区分&#xff1a; SNAT将源IP地址替换为出口网络的IP地址&#xff0c;以便内网地址可以访问外网服务。一般受限于公网IP有限&#xff0c;一个内网集合想访问外网服务&#xff0c;则用统一的出口做代理。出口配置公网IP&#xff0c;帮助从此发出的…