Java 实现自定义注解

一、@interface 关键字

我们想定义一个自己的注解 需要使用 @interface 关键字来定义。
如定义一个叫 MyAnnotation 的注解:

public @interface MyAnnotation { }

 二、元注解

  光加上 @interface 关键字 还不够,我们还需要了解5大元注解

  • @Retention
  • @Target
  • @Documented
  • @Inherited(JDK8 引入)
  • @Repeatable(JDK8 引入)

 1)  @Retention 指定注解的生命周期    

@Retention(RetentionPolicy.SOURCE)

其中Retention是一个枚举类:

  1. RetentionPolicy.SOURCE : 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃(.java文件)
  2. RetentionPolicy.CLASS :注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期(.class文件)
  3. RetentionPolicy.RUNTIME: 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在(内存中的字节码)

2) @Target指定注解可以修饰的元素类型

@Target(ElementType.Field)
  1. ElementType.ANNOTATION_TYPE - 标记的注解可以应用于注解类型。
  2. ElementType.CONSTRUCTOR - 标记的注解可以应用于构造函数。
  3. ElementType.FIELD - 标记的注解可以应用于字段或属性。
  4. ElementType.LOCAL_VARIABLE - 标记的注解可以应用于局部变量。
  5. ElementType.METHOD - 标记的注解可以应用于方法。
  6. ElementType.PACKAGE - 标记的注解可以应用于包声明。
  7. ElementType.PARAMETER - 标记的注解可以应用于方法的参数。
  8. 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) {Class<? 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();}}

 执行后打印的内容:

        以上就是一个注解的简单实现。

四、使用切面执行自定义注解逻辑

        在开发中一般加上注解之后会自动执行一些逻辑,大部分实现的原理是使用切面来实现注解的逻辑的。

       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;}

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

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

相关文章

JavaWeb--JavaScript-事件绑定/BOM/DOM编程

目录 1. 事件绑定 1.1. 什么是事件 1.2. 常见事件 1.3. 事件的绑定 1.3.1. 属性绑定 1.3.2. DOM编程绑定 1.4. 事件的触发 1.4.1. 行为触发 1.4.2. DOM编程触发 2. BOM 编程 2.1. 什么是 BOM 2.2. window对象的常见属性(了解) 2.3. window对象的常见方法(了解) 2…

YOLOv8绝缘子边缘破损检测系统(可以从图片、视频和摄像头三种方式检测)

可检测图片和视频当中出现的绝缘子和绝缘子边缘是否出现破损&#xff0c;以及自动开启摄像头&#xff0c;进行绝缘子检测。基于最新的YOLO-v8训练的绝缘子检测模型和完整的python代码以及绝缘子的训练数据&#xff0c;下载后即可运行。&#xff08;效果视频&#xff1a;YOLOv8绝…

softmax回归:多分类问题的解码器

随着人工智能技术的不断发展&#xff0c;分类问题在机器学习领域中的地位日益凸显。在众多分类算法中&#xff0c;softmax回归以其独特的优势和广泛的应用场景&#xff0c;成为了处理多分类问题的有力工具。本文将深入探讨softmax回归的原理、应用及其优缺点&#xff0c;以期为…

使用uniapp实现小程序获取wifi并连接

Wi-Fi功能模块 App平台由 uni ext api 实现&#xff0c;需下载插件&#xff1a;uni-WiFi 链接&#xff1a;https://ext.dcloud.net.cn/plugin?id10337 uni ext api 需 HBuilderX 3.6.8 iOS平台获取Wi-Fi信息需要开启“Access WiFi information”能力登录苹果开发者网站&…

Word wrap在计算机代表的含义(自动换行)

“Word wrap”是一个计算机术语&#xff0c;用于描述文本处理器在内容超过容器边界时自动将超出部分转移到下一行的功能。在多种编程语言和文本编辑工具中&#xff0c;都有实现这一功能的函数或选项。 在编程中&#xff0c;例如某些编程语言中的wordwrap函数&#xff0c;能够按…

数仓维度建模

维度建模 数仓建模方法1. 范式建模法&#xff08;Third Normal Form&#xff0c;3NF&#xff09;2. 维度建模法&#xff08;Dimensional Modeling&#xff09;3. 实体建模法&#xff08;Entity Modeling&#xff09; 维度建模1. 事实表事实表种类事务事实表周期快照事实表累计快…

洛谷-P1596 [USACO10OCT] Lake Counting S

P1596 [USACO10OCT] Lake Counting S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) #include<bits/stdc.h> using namespace std; const int N110; int m,n; char g[N][N]; bool st[N][N]; //走/没走 int dx[] {-1,-1,-1,0,0,1,1,1}; //八联通 int dy[] {-1,0,1,1,-1,1…

Matlab调C/C++简单模板例子

如果你是需要快速搭建一个matlab调c/c环境&#xff0c;这篇文章可以参考 有了c代码&#xff0c;想在matlab里面调用&#xff0c;可以参考我这个模板 matlab调用代码&#xff1a; clear all close all clcinput1 1; input2 2;[output1,output2] mexfunction(input1,input2);…

GitHub repository - Branch - SSH clone URL - Clone in Desktop - Download ZIP

GitHub repository - Branch - SSH clone URL - Clone in Desktop - Download ZIP 1. Branch2. SSH clone URL3. Clone in Desktop4. Download ZIPReferences 1. Branch 显示当前分支的名称。从这里可以切换仓库内分支&#xff0c;查看其他分支的文件。 2. SSH clone U…

Vue.js组件精讲 第2章 基础:Vue.js组件的三个API:prop、event、slot

如果您已经对 Vue.js 组件的基础用法了如指掌&#xff0c;可以跳过本小节&#xff0c;不过当做复习稍读一下也无妨。 组件的构成 一个再复杂的组件&#xff0c;都是由三部分组成的&#xff1a;prop、event、slot&#xff0c;它们构成了 Vue.js 组件的 API。如果你开发的是一个…

52.网络游戏逆向分析与漏洞攻防-基础数据分析筛选-面对庞大的数据如何找到节奏

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 内容参考于&#xff1a;如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果&…

.NET SignalR Redis实时Web应用

环境 Win10 VS2022 .NET8 Docker Redis 前言 什么是 SignalR&#xff1f; ASP.NET Core SignalR 是一个开放源代码库&#xff0c;可用于简化向应用添加实时 Web 功能。 实时 Web 功能使服务器端代码能够将内容推送到客户端。 适合 SignalR 的候选项&#xff1a; 需要从服…

二、Maven安装

Maven安装 一、Centos7.9安装1.下载2.安装3.设置国内镜像4.设置maven安装路径 一、Centos7.9安装 1.下载 第一种&#xff1a;官网下载最新版本&#xff1a;http://maven.apache.org/download.cgi第二种&#xff1a;其他版本下载&#xff1a;https://archive.apache.org/dist/…

K-means和逻辑回归

逻辑回归 一个事件的几率是该事件发生的概率/该事件不发生的概率&#xff1a;P/&#xff08;1-P&#xff09; 对数几率是&#xff1a;log(P/&#xff08;1-P&#xff09;) **考虑对输入x分类的模型&#xff1a;**log(P/&#xff08;1-P&#xff09;)wx 则 Pexp(wx)/(exp(w*x)…

程序员Java.vue,python前端后端爬虫开发资源分享

bat面试资料 bat面试题汇总 提取码&#xff1a;724z 更多资料

【迅为iMX6Q】开发板 Linux version 6.6.3 SD卡 启动

开发环境 win10 64位 VMware Workstation Pro 16 ubuntu 20.04 【迅为imx6q】开发板&#xff0c; 2G DDR RAM linux-imx 下载 使用 NXP 官方提供的 linux-imx&#xff0c;代码地址为&#xff1a; https://github.com/nxp-imx/linux-imx 使用 git 下载 linux-imx&#xff…

Github 2024-04-08 开源项目周报Top15

根据Github Trendings的统计,本周(2024-04-08统计)共有15个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目7Jupyter Notebook项目2TypeScript项目2C项目1Shell项目1C++项目1JavaScript项目1Mojo项目1Rust项目1非开发语言项目1编程面试大学:…

LinkedList部分底层源码分析

JDK版本为1.8.0_271&#xff0c;以插入和删除元素为例&#xff0c;LinkedList部分源码如下&#xff1a; //属性&#xff0c;底层结构为双向链表 transient Node<E> first; //记录第一个结点的位置 transient Node<E> last; //记录最后一个结点的尾元素 transient …

vim卡死了,没有反应怎么办?

解决办法&#xff1a; 很有可能是你有个在window下的好习惯&#xff0c;没事儿就ctrl s保存文件。但是在vim里&#xff0c;ctrl s默认是发送一种流控制信号&#xff0c;通常用于停止终端的输出&#xff0c;所以你的屏幕就卡死了。 解决办法也很简单&#xff0c;按下ctrl q即…

XILINX 7系列时钟资源

文章目录 前言一、时钟概要1.1、CC1.2、BUFR、BUFIO、BUFMR1.3、CMT1.4、BUFH1.5、BUFG 二、时钟路由资源三、CMT 前言 本文主要参考xilinx手册ug472 一、时钟概要 7系列FPGA时钟资源主要有CC、BUFR、BUFIO、BUFMR、CMT、BUFG、BUFH和GTE_COMMON 1.1、CC “CC”&#xff0…