恰逢最近有机会用到自定义注解,整理最近学习到的知识在这里做一个持久化输出。
1.自定义注解介绍
1.使用@interface声明一个注解类
3.自定义注解时,需要使用元注解。官方提供的注解有以下几种:
| 基本注解 | 元注解(修饰其它注解) |
|---|---|
| 所属包:java.lang | 所属包:java.lang.annotation |
| @Override | @Retention |
| @Deprecated | @Target |
| @SuppressWarnings | @Documented |
| @FunctionalInterface(1.8) | @Inherited |
| @SafeVarargs | @Repeatable (1.8) |
简单介绍一下创建自定义注解时常用的注解:
1.1 @Target 表示该注解目标,可能的 ElemenetType 参数包括
- ElemenetType.CONSTRUCTOR 构造器声明
- ElemenetType.FIELD 域声明(包括 enum 实例)
- ElemenetType.LOCAL_VARIABLE 局部变量声明
- ElemenetType.METHOD 方法声明
- ElemenetType.PACKAGE 包声明
- ElemenetType.PARAMETER 参数声明
- ElemenetType.TYPE 类,接口(包括注解类型)或enum声明
1.2 @Retention 表示该注解的生命周期,可选的 RetentionPolicy 参数包括
- RetentionPolicy.SOURCE 注解将被编译器丢弃
- RetentionPolicy.CLASS 注解在class文件中可用,但会被JVM丢弃
- RetentionPolicy.RUNTIME JVM将在运行期也保留注释,因此可以通过反射机制读取注解的信息
1.3 @Inherited 使注解具有继承性。子类会带有父类中被@Inherited修饰的注解。
1.4 @Documented 指示将此注解包含在 javadoc 中
1.5 @Repeatable 想在一处同时使用多个相同的注解时,需要新建数组注解类,并在原注解类上使用@Repeatable声明数组注解类。此时通过反射获取多个相同注解的时候,得到的是一个数组注解类,而不是多个原注解类。
这一段比较复杂,使用一个例子说明。首先定义一个注解类:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
String key();
String value();
}
此时想使用多个该注解时会报错
@TestAnnotation(key = "key1",value = "value1")
@TestAnnotation(key = "key2",value = "value2")//该行报错,错误信息见下行
//Duplicate annotation. The declaration of 'TestAnnotation' does not have a valid
//java.lang.annotation.Repeatable annotation
public class Person {
private String name;
}
想要修复这个报错需要两步,1.新建该注解的数组注解类,2.对TestAnnotation注解添加@Repeatable。
- 新建一个数组注解类如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestArrayAnnotation {
TestAnnotation[] value();
}
- 修改TestAnnotation注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(TestArrayAnnotation.class)//添加这行代码
public @interface TestAnnotation {
String key();
String value();
}
这时就大功告成了。使用反射获取一下我们添加的注解信息:
public class Test {
public static void main(String[] args) {
Class clazz = Person.class;
Annotation[] annotations = clazz.getAnnotations();
System.out.println(annotations.length + " | " + Arrays.toString(annotations));
Annotation[] annotationsByType = clazz.getDeclaredAnnotationsByType(TestAnnotation.class);
System.out.println(annotationsByType.length + " | " + Arrays.toString(annotationsByType));
}
}
执行后,输出结果为:
1 | [@com.business.TestArrayAnnotation(value={@com.business.TestAnnotation(key="key1", value="value1"), @com.business.TestAnnotation(key="key2", value="value2")})]
2 | [@com.business.TestAnnotation(key="key1", value="value1"), @com.business.TestAnnotation(key="key2", value="value2")]
需要注意两种方法获取到的对象不同,下面会讲到。
此外在定义TestArrayAnnotation时,生命周期@Retention和可修饰范围@Target都要比TestAnnotation注解的要大。否则在使用多个TestAnnotation注解时会因为找不到对应的数组注解 类而报错。
2.自定义注解获取
获取类、字段、方法上对应的注解:
public class Test {
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException {
Class clazz = Person.class;
Annotation[] clazzAnnotations = clazz.getAnnotations();//类注解
Method method = clazz.getMethod("getName");
Annotation[] methodAnnotations = method.getAnnotations();//方法注解
Field field = clazz.getField("name");
Annotation[] fieldAnnotations = field.getAnnotations();//字段注解
}
}
注意事项
反射获取注解时要使用对应的方法,下面会做介绍。
引用自java 获取注解信息的方法 小结
对于获取注解时,有4种情况:
| Directly Present | 直接存在 | 注解直接修饰 |
| Indirectly Present | 间接存在 | 注解直接修饰并且该注解被@Repeatable 修饰 |
| Present | 存在 | 注解直接修饰/来自父类的注解 |
| Associated | 关联 | 直接或间接存在/父类直接或间接存在 |
反射方法和获取对应的关系:
| Method | Directly Present | Indirectly Present | Present | Associated |
| T getDeclaredAnnotation(Class) | X | |||
| Annotation[] getDeclaredAnnotations() | X | |||
| T[] getDeclaredAnnotationsByType(Class) | X | X | ||
| T getAnnotation(Class) | X | |||
| Annotation[] getAnnotations() | X | |||
| T[] getAnnotationsByType(Class) | X |
虽然有四种关系,实际上都是基于两种关系(直接存在,间接存在)推演的。
| Method | Directly Present | Indirectly Present | inherit Directly Present | inherit Indirectly Present |
| T getDeclaredAnnotation(Class) | X | |||
| Annotation[] getDeclaredAnnotations() | X | |||
| T[] getDeclaredAnnotationsByType(Class) | X | X | ||
| T getAnnotation(Class) | X | X | ||
| Annotation[] getAnnotations() | X | X | ||
| T[] getAnnotationsByType(Class) | X | X | X | X |
命名规则对应关系:
- getDeclaredAnnotationXXXX :只可以获取直接存在注解信息
- getXXXXByType : 可以获取间接存在注解信息
- getAnnotationXXX : 可以获取继承注解信息
- 所有的方法都可以获取直接注解信息
3.注解原理
本人能力有限,看到这篇写的非常不错,推荐给大家- Java注解(Annotation)原理详解


被折叠的 条评论
为什么被折叠?



