Java自定义注解实现

news2024/11/25 11:04:17

一、注解的定义和作用

1、定义
  注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

2、作用
(1)编写文档:
  通过代码里标识的元数据生成文档【生成文档doc文档】
(2)代码分析:
  通过代码里标识的元数据对代码进行分析【使用反射】
(3)编译检查:
  通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】

二、元注解

注解说明
@Target(JDK1.5)定义注解的作用目标
@Retention(JDK1.5)定义注解的保留策略
@Document(JDK1.5)注解是否应当被包含在 JavaDoc 文档中(生成说明文档,添加类的解释 )
@Inherited(JDK1.5)说明子类可以继承父类中的该注解
@Repeatable(JDK1.8)允许在相同的程序元素中重复注解,在需要对同一注解多次使用时,往往需要借助@Repeatable注解。
@Native(JDK1.8)修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。

1、Target类型说明

Target类型说明
ElementType.TYPE类、接口(包括注释类型)或枚举声明
ElementType.FIELD字段声明(包括枚举常量)
ElementType.METHOD方法声明
ElementType.PARAMETER正式的参数声明
ElementType.CONSTRUCTOR构造函数声明
ElementType.LOCAL_VARIABLE局部变量声明
ElementType.ANNOTATION_TYPE注解类型声明
ElementType.PACKAGE程式包说明

2、Retention类型说明

Target类型说明
RetentionPolicy.SOURCE注解仅存在于源码中,在class字节码文件中不包含
RetentionPolicy.CLASS默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
RetentionPolicy.RUNTIME注解会在class字节码文件中存在,在运行时可以通过反射获取到

3、@Documented和@Inherited的比较
  (1)@Documented注解修饰的注解,当我们执行 JavaDoc 文档打包时会被保存进 doc 文档,反之将在打包时丢弃。
  (2)@Inherited注解修饰的注解是具有可继承性的,也就说我们的注解修饰了一个类,而该类的子类将自动继承父类的该注解。

4、注意
  (1)@Repeatable 所声明的注解,其元注解@Target的使用范围要比@Repeatable的值声明的注解中的@Target的范围要大或相同,否则编译器错误,显示@Repeatable值所声明的注解的元注解@Target不是@Repeatable声明的注解的@Target的子集。
  (2)@Repeatable注解声明的注解的元注解@Retention的周期要比@Repeatable的值指向的注解的@Retention得周期要小或相同。
周期长度为:SOURCE(源码) < CLASS (字节码) < RUNTIME(运行)

5、实例

//Java 8 之前的做法
public @interface Roles {
    Role[] roles();
}

public @interface Role {
    String roleName();
}

public class RoleTest {
    @Roles(roles = {@Role(roleName = "role1"), @Role(roleName = "role2")})
    public String doString(){
        return "这是C语言中国网Java教程";
    }
}

//java 8 之后增加了重复注解
public @interface Roles {
    Role[] value();
}

@Repeatable(Roles.class)
public @interface Role {
    String roleName();
}

public class RoleTest {
    @Role(roleName = "role1")
    @Role(roleName = "role2")
    public String doString(){
        return "这是C语言中文网Java教程";
    }
}

三、自定义注解实现

1、运行时处理的注解(反射机制)
(1)定义注解

package com.newtouch.springboot.examples.annotation.runningAnnotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 定义注解
 * 运行时处理的注解(反射机制)
 */
@Target(ElementType.METHOD)//作用目标是方法
@Retention(RetentionPolicy.RUNTIME)//保留策略使用RUNTIME:注解会在class字节码文件中存在,在运行时可以通过反射获取到
public @interface Reflect {
    String name() default "liuyifei";
}

(2)注解处理器

package com.newtouch.springboot.examples.annotation.runningAnnotation;

import java.lang.reflect.Method;

/**
 * 注解处理器
 */
public class ReflectProcessor {
    public void parseMethod(final Class<?> clazz) throws Exception {
        final Object obj = clazz.getConstructor(new Class[] {}).newInstance(new Object[] {});
        final Method[] methods = clazz.getDeclaredMethods();
        for (final Method method : methods) {
            final Reflect my = method.getAnnotation(Reflect.class);
            if (null != my) {
                System.out.println("方法名:");
                System.out.println(method.getName());
                method.invoke(obj, my.name());
            }
        }
    }
}

(3)测试注解

package com.newtouch.springboot.examples.annotation.runningAnnotation;

/**
 * 测试注解
 */
public class ReflectTest {
    @Reflect
    public static void sayHello1(final String name) {
        System.out.println("==>> Hi, " + name + " , nest to meet you!");
    }

    @Reflect(name = "yangmi")
    public static void sayHello2(final String name) {
        System.out.println("==>> Hi, " + name + " , nest to meet you!");
    }

    public static void main(final String[] args) throws Exception {
        final ReflectProcessor reflectProcessor = new ReflectProcessor();
        reflectProcessor.parseMethod(ReflectTest.class);
    }
}

(4)测试结果

方法名:
sayHello1
==>> Hi, liuyifei , nest to meet you!
方法名:
sayHello2
==>> Hi, yangmi , nest to meet you!

2、编译时处理的注解(虚处理器方式AbstractProcessor)
(1)定义注解

package com.newtouch.springboot.examples.annotation.compileAnnotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 定义注解
 * 编译时处理的注解(虚处理器方式AbstractProcessor)
 */
@Target({ElementType.TYPE, ElementType.METHOD,ElementType.FIELD})//类、接口(包括注释类型)或枚举声明;方法声明;字段声明(包括枚举常量)
@Retention(RetentionPolicy.SOURCE)//保留策略使用SOURCE:注解仅存在于源码中,在class字节码文件中不包含
public @interface NameScanner {
}

(2)注解处理器

package com.newtouch.springboot.examples.annotation.compileAnnotation;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.util.Set;

/**
 * 注解处理器
 * 将每一个被注解的元素名称打印出来
 */
public class NameScannerProcessor extends AbstractProcessor {

    /**
     * 该方法主要用于一些初始化的操作,通过该方法的参数ProcessingEnvironment可以获取一系列有用的工具类。
     * @param processingEnv
     */
    @Override
    public void init(ProcessingEnvironment processingEnv){
        super.init(processingEnv);
    }

    /**
     * 注解处理器的核心方法,处理具体的注解。主要功能基本可以理解为两个:
     *  1、获取同一个类中的所有指定注解修饰的Element;
     *      a、set参数,存放的是支持的注解类型,一般无用。
     *      b、RoundEnvironment参数,可以通过遍历获取代码中所有通过指定注解(例如在ButterKnife中主要就是@BindeView等)修饰的Element对象。通过Element对象可以获取字段名称,字段类型以及注解元素的值。
     *  2、创建Java文件;
     *      将同一个类中通过指定注解修饰的所有Element在同一个Java文件中实现初始化,这样做的目的是让在最终依赖注入时便于操作。
     * @param annotations
     * @param roundEnv
     * @return
     */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
        if(!roundEnv.processingOver()){
            for(Element element : roundEnv.getElementsAnnotatedWith(NameScanner.class)){
                String name = element.getSimpleName().toString();
                processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "element name: " + name);
            }
        }
        return false;
    }
}

(3)测试注解

package com.newtouch.springboot.examples.annotation.compileAnnotation;

@NameScanner
public class NameScannerTest {
    @NameScanner
    private String name;

    @NameScanner
    private int age;

    @NameScanner
    public String getName(){
        return this.name;
    }

    @NameScanner
    public void setName(String name){
        this.name = name;
    }

    public static void main(String[] args){
        System.out.println("--finished--");
    }
}

(4)手动编译

javac NameScanner.java
javac NameScannerProcessor.java
javac -processor NameScannerProcessor NameScannerTest.java

编译输出:
element name: NameScannerTest
element name: name
element name: age
element name: getName
element name: setName

3、AbstractProcessor方法作用解析
(1)void init(ProcessingEnvironment processingEnv)
  该方法主要用于一些初始化的操作,通过该方法的参数ProcessingEnvironment可以获取一系列有用的工具类
(2)SourceVersion getSupportedSourceVersion()
  返回此注释 Processor 支持的最新的源版本,该方法可以通过注解@SupportedSourceVersion指定。
(3)Set getSupportedAnnotationTypes()
  返回此 Processor 支持的注释类型的名称。结果元素可能是某一受支持注释类型的规范(完全限定)名称。它也可能是 ” name.” 形式的名称,表示所有以 ” name.” 开头的规范名称的注释类型集合。最后,自身表示所有注释类型的集合,包括空集。
注意:
  Processor不应声明 “*”,除非它实际处理了所有文件;声明不必要的注释可能导致在某些环境中的性能下降。
(4)boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment)
  注解处理器的核心方法,处理具体的注解。主要功能基本可以理解为两个:
1)获取同一个类中的所有指定注解修饰的Element
  set参数,存放的是支持的注解类型,一般无用。
  RoundEnvironment参数,可以通过遍历获取代码中所有通过指定注解(例如在ButterKnife中主要就是@BindeView等)修饰的Element对象。通过Element对象可以获取字段名称,字段类型以及注解元素的值。
2)创建Java文件
  将同一个类中通过指定注解修饰的所有Element在同一个Java文件中实现初始化,这样做的目的是让在最终依赖注入时便于操作。

4、Java代码编译过程与注解处理器的注解处理过程
(1)Java代码编译过程
1)解析与填充符号表过程;
2)插入式注解处理器的注解处理过程;
3)语义分析与字节码生成过程。
在这里插入图片描述
(2)注解处理器的注解处理过程
  插入式注解处理器可以在编译器时读取、修改、添加抽象语法树中的任意元素。如果这些插件在处理注解期间对语法树进行了修改,那么编译器将回到解析及填充符号表的过程重新处理,直到所有的插入式注解处理器都没有再对语法树进行修改为止。

参考文献:
https://blog.csdn.net/qq_16268979/article/details/108251227
https://blog.csdn.net/doc_sgl/article/details/50367083
https://www.jianshu.com/p/1acb3cd1fc8f

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

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

相关文章

51的原理图和pcb的注意事项及出现的问题与解决办法

本次总结基于立创eda的注意事项总结&#xff0c;如有疑问&#xff0c;欢迎交流。 1.焊盘设成地网络&#xff01;&#xff01;&#xff01;&#xff01; 2.pcb的基本步骤是布局---布线---泪滴---铺铜---缝合孔&#xff0c;记得泪滴和缝合孔!! 3.元器件的封装要选择贴片的 这样…

认识阻塞队列

认识阻塞队列一、相关概念1.1 阻塞队列是什么1.2 生产者消费者模型二、标准库中的阻塞队列2.1 使用2.2 生产者消费者模型实现三、实现阻塞队列3.1 循环队列3.2 实现的细节3.3 代码一、相关概念 1.1 阻塞队列是什么 阻塞队列是一种特殊的队列&#xff0c;也遵守 “先进先出” …

CENTOS上的网络安全工具(十六)容器特色的Linux操作

这一篇&#xff0c;我们继续在Docker上折腾。之前我们已经展示了如何在容器上搭建安全产品的部署环境&#xff0c;这里我们需要更进一步&#xff0c;讨论如何在容器上搭建开发与调试环境。这是学习安全产品并且自己构建安全产品的基础步骤。 〇、精简系统上的操作技巧 使用Dock…

【LeetCode】直线上最多的点数 [H](几何)

149. 直线上最多的点数 - 力扣&#xff08;LeetCode&#xff09; 一、题目 给你一个数组 points &#xff0c;其中 points[i] [xi, yi] 表示 X-Y 平面上的一个点。求最多有多少个点在同一条直线上。 示例 1&#xff1a; 输入&#xff1a;points [[1,1],[2,2],[3,3]] 输出&a…

【数据结构与算法分析】0基础带你学数据结构与算法分析11--AVL树

目录 二分查找 AVL 树 AVL 的平衡因子 AVL 的插入操作 单旋转 双旋转 对 AVL 树插入的总结 AVL 的移除操作 如果给定一个序列&#xff0c;你将如何在这个序列中查找一个给定元素 target&#xff0c;当找到时返回该元素的迭代器&#xff0c;否则返回末尾迭代器。首先排除…

python基于flask搭建http服务(二)—— 实现Excel上传、数据清洗、入库

一、技术点 利用flask 搭建简易的http服务,实现服务端文件上传;利用Blueprint将不同业务拆分至不同文件;利用bootstrap-fileinput组件构建页面;利用flask_cors配置允许跨域请求;利用sqlalchemy实现数据库连接;利用pandas实现Excel读取、清洗、入库;二、具体实现 2.1 目…

基于intel i3/i5/i7 视觉控制器 4个POE GigE

XM-5143 是针对机器视觉行业GigE相机应用场景的一款高性能工业计算机&#xff0c;可使用Intel 6/7代 65W 高性能CPU应对快速检测。4路独立Intel网络芯片&#xff0c;并支持POE供电&#xff0c;可减少布线。并配有快速隔离可编程DI/O芯片. XM-5143的规格产品类型机器视觉控制器防…

分布式事务都有哪些,到底有什么用,在项目当中该用哪个?

分布式的CAP理论应该是人尽皆知了&#xff0c;它描述了一致性&#xff08;C&#xff09;、可用性&#xff08;A&#xff09;、分区容错性&#xff08;P&#xff09;的一系列权衡。很多时候&#xff0c;我们要在一致性和可用性之间权衡&#xff0c;而分布式事务&#xff0c;就是…

20.EC实战 笔记本电脑的休眠唤醒是如何实现的

文章目录 1. EC什么时候(收到什么命令)之后来执行S0-->S3下电时序?2. S3(休眠)状态应该保留哪些电源?3. 控制笔记本进入休眠状态的三种方式总结:前言: 最近很多朋友在咨询休眠唤醒的问题,在笔记本中,休眠唤醒是一个基本功能,如果连休眠唤醒都没有,就失去了笔记本…

Volatile内存语义以及如何正确使用

目录 语义描述 重排序规则 JMM如何实现volatile语义 如何正确使用volatile变量 语义描述 写语义&#xff1a;当写一个volatile变量时候&#xff0c;JMM会把该线程对应的本地内存的共享变量直接刷新到主内存中。 读语义&#xff1a;当读一个volatile变量的时候。JMM会把该线…

分布式锁主动续期的入门级实现-自省 | 简约而不简单

一、背景 如果某个客户端获得锁之后处理时间超过最大约定时间&#xff0c;或者持锁期间内发生了故障导致无法主动释放锁&#xff0c;其持有的锁也能够被其他机制正确释放&#xff0c;并保证后续其它客户端也能加锁&#xff0c;整个处理流程继续正常执行。 简单解释一下&#xf…

LLeetCode题目笔记——6258. 数组中最长的方波

文章目录题目描述题目难度——中等方法一&#xff1a;一次遍历哈希代码Python暴力代码Python一次遍历总结题目描述 这是这周周赛的第二题&#xff0c;本来想是不是双指针的解法&#xff0c;但想半天没想出来咋用双指针&#xff0c;就想到了哈希。 给你一个整数数组 nums 。如果…

PostgreSQL数据库TPCC测试,Banchmarksql 5.0部署详解

1 BenchmarkSQL安装部署 1.1 部署Java环境 首先使用java - version查看是否已有 Java环境&#xff08;下图是有的情况&#xff09; 需要注意的是&#xff0c;虚拟机中默认的JDK貌似是不行的哦&#xff0c;请参考下面链接中的博文&#xff0c;教你怎么卸载重装 1.1.1 若没有J…

Python学习基础笔记四十四——模块1

1、看一个例子&#xff1a; 创建一个demo.py文件&#xff1a; print(in demo.py)def hello():print(in hello function) 然后我们在另外一个文件中import这个demo文件&#xff1a; import demo# 调用demo.py文件中的hello()函数 demo.hello() 注意&#xff0c;demo后面没有…

软件安全测试-BurpSuite使用详解

1.BurpSuite简介 Burp Suite 是用于攻击web 应用程序的集成平台&#xff0c;它包含了许多Burp工具&#xff0c;这些不同的burp工具通过协同工作&#xff0c;有效的分享信息&#xff0c;支持以某种工具中的信息为基础供另一种工具使用的方式发起攻击。 它主要用来做安全性渗透测…

我将 9 个 ChatGPT 账号接入微信,我现在整个人都麻了...

大家好&#xff0c;我是米开朗基杨。最近大家都被 ChatGPT 刷屏了&#xff0c;这家伙真是上天入地无所不能&#xff0c;不管什么问题都能解答&#xff0c;而且答案的质量非常高&#xff0c;完全不像机器人。于是乎我冒出个想法&#xff1a;如果把 ChatGPT 接入微信是什么感觉&a…

二维码介绍

二维码介绍 二维码&#xff08;2-dimensional bar code、二维条码&#xff09;是用某种特定的几何图形按一定规律在平面&#xff08;二维方向上&#xff09;分布的、黑白相间的、记录数据符号信息的图形&#xff1b;在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”、“1…

repo的安装和使用

前言 Android 采用 Gerrit 提供代码评审服务&#xff0c;并且开发了一个客户端工具 repo&#xff0c;实现多仓库管理。Git 的开发者对服务端的 Git 源码做了扩展&#xff0c;使得基于 Git&#xff08;cgit&#xff09;的代码平台可以很容易引入新的集中式工作流。同样 git-rep…

概率统计·参数估计【矩估计、极大似然估计、无偏性、有效性、相合性】

点估计 设总体的分布函数形式已知&#xff0c;但它的一个或多个参数为未知&#xff0c;借助于总体的一个样本来估计总体未知参数的值的问题称为点估计问题 矩估计 这个还是看例子会比较好理解一些 例 先μ1E(x)&#xff0c;μ2E(x2)有几个未知参数就列几次方的期望&#xff…

CSS -- 01. CSS基础

文章目录CSS基础1 CSS简介1.1 HTML的局限性1.2 CSS介绍1.3 CSS语法规范1.4 CSS代码风格2 CSS基础选择器2.1 选择器的分类2.2 标签选择器2.3 **类选择器**2.4 id选择器2.5 通配符选择器2.6 基础选择器总结3 CSS字体属性3.1 字体系列3.2 字体大小3.3 字体粗细3.4 文字样式3.5 字体…