如果是为了当查询手册可以直接跳到 总结
jdk 内置了一些注解
基本注解
元注解 :用来修饰其他注解的注解。
工作中比较常用的就是元注解就是 @Retention 和 @Target。下面简单介绍我们接下来会用到的
RetentionPolicy 有以下三个值:
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
* @since 1.8
*/
TYPE_USE
}
比如常用注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController
在看mybatis 拦截器注解的时候发现了一个从未见过的类型@Target({})
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature
在网上搜索了一番没有找到相关介绍,就看了一下JDK 源码,发现有这么一段注释。
This @Target meta-annotation indicates that the declared type is intended solely for use as a member type in complex annotation type declarations. It cannot be used to annotate anything directly:
@Target({})
public @interface MemberType {
…
}
就是说使用 @Target({})
定义的注解,只能作为其他注解的一个成员。官方诚不欺我。
下面是Mybatis 定义拦截器的注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {
Signature[] value();
}
@Inherited 所修饰的注解 将具有继承性。例如B继承了A,A添加了注解C(该注解被@Inherited修饰),那么B将会具有A注解
注解通常只能直接修饰某个元素一次,如果修饰了多次那么会出现编译异常
public @interface Role {
String role() default "";
}
如果想要某个注解在某个元素上(直接)出现多次,那么就可以使用@Repeatable
注解:
@Target(ElementType.ANNOTATION_TYPE)
】Class extends Annotation> value()
】注意:
关于第3条,并不是说 包含多个注解的容器注解 不包含 value成员就会报错(编译错误),而是不包含value 成员会导致某些方法不用使用,详情请看 ,↓ getDeclaredAnnotationsByType 源码解析@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Roles {
//value属性必须存在,指定Role 属性可以重复存在
Role[] value();
}
@Repeatable(Roles.class)
public @interface Role {
String role() default "";
}
@Role("role1")
@Role("role2")
@Role("role3")
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
这其实是间接存在的注解,可以通过下面的方法获取注解信息
@Test
public void indriectAnnotation(){
//间接存在
Arrays.asList(Person.class.getDeclaredAnnotationsByType(Role.class)).forEach(action-> {
if (action instanceof Role) {
System.out.println("getDeclaredAnnotationsByType\t"+((Role) action).value());
}
});
//关联
Arrays.asList(Person.class.getAnnotationsByType(Role.class)).forEach(action-> {
if (action instanceof Role) {
System.out.println("getAnnotationsByType\t"+((Role) action).value());
}
});
}
getDeclaredAnnotationsByType role1
getDeclaredAnnotationsByType role2
getDeclaredAnnotationsByType role3
getAnnotationsByType role1
getAnnotationsByType role2
getAnnotationsByType role3
当然除了上面的定义方式,还可以选择下面的方式:
@Roles({@Role("role1"),@Role("role2")})
public class Person{}
自定义注解使用 @interface 关键字,定义注解和定义接口的方式有些类似。实际上所有的注解类定义都实现了Annotation接口
public @interface HelloWord{
String say() default "hello_word";
}
注解本质是一个继承了Annotation的特殊接口,实现原理在Java运行时生成注解的动态代理类。
AnnotationParser.class
public static Annotation annotationForMap(final Class<? extends Annotation> var0, final Map<String, Object> var1) {
return (Annotation)AccessController.doPrivileged(new PrivilegedAction<Annotation>() {
public Annotation run() {
return (Annotation)Proxy.newProxyInstance(var0.getClassLoader(), new Class[]{var0}, new AnnotationInvocationHandler(var0, var1));
}
});
}
在java.lang.reflect 包下的所有程序元素都实现了AnnotatedElement接口。
实现类:
Represents an annotated element of the program currently running in this VM. This interface allows annotations to be read reflectively. All annotations returned by methods in this interface are immutable and serializable
AnnotatedElement表示当前在此VM中运行的程序的注释元素,AnnotatedElement接口允许以反射方式读取注解信息。
如果注解被RetentionPolicy.RUNTIME修饰,就可以通过反射机制来获取注解信息了。
AnnotatedElement 接口提供了多种方法来获取不同定义方式下的注解。(不知道怎么翻译比较好,就直译了,或许 “修饰”、“呈现” 比较好些)
@Repeatable
修饰,并且该注解直接修饰在元素上(类,属性,方法,注解)官方对于直接存在(Directly Present)的定义:
An annotation A is directly present on an element E if E has a RuntimeVisibleAnnotations or RuntimeVisibleParameterAnnotations or RuntimeVisibleTypeAnnotations attribute, and the attribute contains A.
如果E具有RuntimeVisibleAnnotations或RuntimeVisibleParameterAnnotations或RuntimeVisibleTypeAnnotations属性,并且该属性包含A,那么则称注解A 修饰 元素B。
看到这我就一脸懵逼了,然后就各种查资料,然后也没有找到关于这方面的直接资料。但是根据使用经验来说getDeclaredAnnotation 方法可以获取直接修饰在某在元素上的注解。
我不死心的搜索了RuntimeVisibleAnnotations ,然后就恍然大悟了。在编译为class文件的时候,在class文件中就会保存这个注解信息.
所以官方的定义简单的来讲就是字面意思,直接存在(直接修饰)。
我们定一个简单的注解
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Inherited
public @interface Name {
String value();
}
一个简单的类
@Name("test")
public class Person1 {
public static void main(String[] args) {
System.out.println("hello");
}
}
首先编译java文件
javac -d . Name.java
javac -d . Person1.java
然后使用javap 命令查看class文件
查看class文件
javap -verbose xxx/xx/Person1
命令窗口输出以下内容:
// javap -verbose a.test.Person1
Classfile /E:/projects/mybatis/target/classes/com/example/annotation/Person1.class
Last modified 2019-7-19; size 602 bytes
MD5 checksum 71a22a00a071e6075f728efac73b5be6
Compiled from "Person1.java"
public class com.example.annotation.Person1
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#24 // java/lang/Object."":()V
#2 = Fieldref #25.#26 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #27 // hello
#4 = Methodref #28.#29 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #30 // com/example/annotation/Person1
#6 = Class #31 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/example/annotation/Person1;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = Utf8 args
#17 = Utf8 [Ljava/lang/String;
#18 = Utf8 SourceFile
#19 = Utf8 Person1.java
#20 = Utf8 RuntimeVisibleAnnotations
#21 = Utf8 Lcom/example/annotation/Name;
#22 = Utf8 value
#23 = Utf8 test
#24 = NameAndType #7:#8 // "":()V
#25 = Class #32 // java/lang/System
#26 = NameAndType #33:#34 // out:Ljava/io/PrintStream;
#27 = Utf8 hello
#28 = Class #35 // java/io/PrintStream
#29 = NameAndType #36:#37 // println:(Ljava/lang/String;)V
#30 = Utf8 com/example/annotation/Person1
#31 = Utf8 java/lang/Object
#32 = Utf8 java/lang/System
#33 = Utf8 out
#34 = Utf8 Ljava/io/PrintStream;
#35 = Utf8 java/io/PrintStream
#36 = Utf8 println
#37 = Utf8 (Ljava/lang/String;)V
{
public com.example.annotation.Person1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
LineNumberTable:
line 8: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/example/annotation/Person1;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String hello
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 10: 0
line 11: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;
}
SourceFile: "Person1.java"
RuntimeVisibleAnnotations:
0: #21(#22=s#23)
在这个class文件的最下方会发现如下代码:
RuntimeVisibleAnnotations:
0: #21(#22=s#23)
通过常量池(Constant pool)中的定义,我们不难拼出如下信息:
Lcom/example/annotation/Name(value=test)
关于RuntimeVisibleAnnotations属性的详细内容可以查看原文:Java注解(Annotation)原理详解
获取该注解的方法:
/**
* Returns annotations that are directly present on this element.
* This method ignores inherited annotations.
*
* If there are no annotations directly present on this element,
* the return value is an array of length 0.
*
* The caller of this method is free to modify the returned array; it will
* have no effect on the arrays returned to other callers.
*
* @return annotations directly present on this element
* @since 1.5
*/
Annotation[] getDeclaredAnnotations();
/**
* Returns this element's annotation for the specified type if
* such an annotation is directly present, else null.
*
* This method ignores inherited annotations. (Returns null if no
* annotations are directly present on this element.)
*
* @implSpec The default implementation first performs a null check
* and then loops over the results of {@link
* #getDeclaredAnnotations} returning the first annotation whose
* annotation type matches the argument type.
*
*/
default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {
Objects.requireNonNull(annotationClass);
// Loop over all directly-present annotations looking for a matching one
for (Annotation annotation : getDeclaredAnnotations()) {
if (annotationClass.equals(annotation.annotationType())) {
// More robust to do a dynamic cast at runtime instead
// of compile-time only.
return annotationClass.cast(annotation);
}
}
return null;
}
注意:
1. 调用上面的两个方法返回的注解,不包含继承的注解(测试发现,可继承的注释并没有包含RuntimeVisibleAnnotations 之类的代码)
2. getDeclaredAnnotation ,该方法返回第一个和参数匹配的注解
/**
* This method ignores inherited annotations. (Returns null if no
* annotations are directly present on this element.)
* /
An annotation A is indirectly present on an element E if E has a RuntimeVisibleAnnotations or RuntimeVisibleParameterAnnotations or RuntimeVisibleTypeAnnotations attribute, and A 's type is repeatable, and the attribute contains exactly one annotation whose value element contains A and whose type is the containing annotation type of A 's type.
总结下来就是:
同样的间接存在 class文件中也存在RuntimeVisibleAnnotations属性,只不过此时的注解必须被@Repeatable修饰。
同样按照上面的方式,在class 字节码文件中可以找到RuntimeVisibleAnnotations属性。该属性值是数组形式
RuntimeVisibleAnnotations:
0: #21(#22=[@#23(#22=s#24),@#23(#22=s#25)])
// 大致编译后的格式
//Lcom/Roles(value=[Lcom/Role(value=role1),Lcom/Role(value=role2)])
注意:
就上面的事例而言:
获取该注解方法:
/**
* Returns this element's annotation(s) for the specified type if
* such annotations are either directly present or
* indirectly present. This method ignores inherited
* annotations.
*
* If there are no specified annotations directly or indirectly
* present on this element, the return value is an array of length
* 0.
*
* The difference between this method and {@link
* #getDeclaredAnnotation(Class)} is that this method detects if its
* argument is a repeatable annotation type (JLS 9.6), and if so,
* attempts to find one or more annotations of that type by "looking
* through" a container annotation if one is present.
*
* The caller of this method is free to modify the returned array; it will
* have no effect on the arrays returned to other callers.
*
* @implSpec The default implementation may call {@link
* #getDeclaredAnnotation(Class)} one or more times to find a
* directly present annotation and, if the annotation type is
* repeatable, to find a container annotation. If annotations of
* the annotation type {@code annotationClass} are found to be both
* directly and indirectly present, then {@link
* #getDeclaredAnnotations()} will get called to determine the
* order of the elements in the returned array.
*
* Alternatively, the default implementation may call {@link
* #getDeclaredAnnotations()} a single time and the returned array
* examined for both directly and indirectly present
* annotations. The results of calling {@link
* #getDeclaredAnnotations()} are assumed to be consistent with the
* results of calling {@link #getDeclaredAnnotation(Class)}.
*
* @param the type of the annotation to query for and return
* if directly or indirectly present
* @param annotationClass the Class object corresponding to the
* annotation type
* @return all this element's annotations for the specified annotation type if
* directly or indirectly present on this element, else an array of length zero
* @throws NullPointerException if the given annotation class is null
* @since 1.8
*/
default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) {
Objects.requireNonNull(annotationClass);
return AnnotationSupport.
getDirectlyAndIndirectlyPresent(Arrays.stream(getDeclaredAnnotations()).
collect(Collectors.toMap(Annotation::annotationType,
Function.identity(),
((first,second) -> first),
LinkedHashMap::new)),
annotationClass);
}
An annotation A is present on an element E if either:
- A is directly present on E; or
- No annotation of A 's type is directly present on E, and E is a class, and A 's type is inheritable, and A is present on the superclass of E.
以下两种情况都称之为存在:
@Inherit
),并且修饰在元素E的父类上(超类)。这种情形不知道该叫 继承直接存在还是 父类直接存在,下面统称为 继承直接存在
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return getAnnotation(annotationClass) != null;
}
/**
* Returns this element's annotation for the specified type if
* such an annotation is present, else null.
*
* @param the type of the annotation to query for and return if present
* @param annotationClass the Class object corresponding to the
* annotation type
* @return this element's annotation for the specified annotation type if
* present on this element, else null
*/
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
/**
* Returns annotations that are present on this element.
*
* If there are no annotations present on this element, the return
* value is an array of length 0.
*
* The caller of this method is free to modify the returned array; it will
* have no effect on the arrays returned to other callers.
*/
Annotation[] getAnnotations();
/**
* Returns true if an annotation for the specified type
* is present on this element, else false. This method
* is designed primarily for convenient access to marker annotations.
*
* The truth value returned by this method is equivalent to:
* {@code getAnnotation(annotationClass) != null}
*
*
The body of the default method is specified to be the code
* above.
*
* @param annotationClass the Class object corresponding to the
* annotation type
* @return true if an annotation for the specified annotation
* type is present on this element, else false
* @throws NullPointerException if the given annotation class is null
* @since 1.5
*/
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return getAnnotation(annotationClass) != null;
}
以下两种情况都称之为关联:
获取关联关系注解的方法
/**
* Returns annotations that are associated with this element.
*
* If there are no annotations associated with this element, the return
* value is an array of length 0.
*
* The difference between this method and {@link #getAnnotation(Class)}
* is that this method detects if its argument is a repeatable
* annotation type (JLS 9.6), and if so, attempts to find one or
* more annotations of that type by "looking through" a container
* annotation.
*
* The caller of this method is free to modify the returned array; it will
* have no effect on the arrays returned to other callers.
*
* @implSpec The default implementation first calls {@link
* #getDeclaredAnnotationsByType(Class)} passing {@code
* annotationClass} as the argument. If the returned array has
* length greater than zero, the array is returned. If the returned
* array is zero-length and this {@code AnnotatedElement} is a
* class and the argument type is an inheritable annotation type,
* and the superclass of this {@code AnnotatedElement} is non-null,
* then the returned result is the result of calling {@link
* #getAnnotationsByType(Class)} on the superclass with {@code
* annotationClass} as the argument. Otherwise, a zero-length
* array is returned.
*
* @param the type of the annotation to query for and return if present
* @param annotationClass the Class object corresponding to the
* annotation type
* @return all this element's annotations for the specified annotation type if
* associated with this element, else an array of length zero
* @throws NullPointerException if the given annotation class is null
* @since 1.8
*/
default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {
/*
* Definition of associated: directly or indirectly present OR
* neither directly nor indirectly present AND the element is
* a Class, the annotation type is inheritable, and the
* annotation type is associated with the superclass of the
* element.
*/
T[] result = getDeclaredAnnotationsByType(annotationClass);
// 如果注解不是直接存在和间接存在,但是注解是可继承的那么寻找该类的父类是否存在该注解
if (result.length == 0 && // Neither directly nor indirectly present
this instanceof Class && // the element is a class
AnnotationType.getInstance(annotationClass).isInherited()) { // Inheritable
Class<?> superClass = ((Class<?>) this).getSuperclass();
if (superClass != null) {
// Determine if the annotation is associated with the
// superclass
result = superClass.getAnnotationsByType(annotationClass);
}
}
return result;
}
下面的表格从来官方文档,总结的特别好
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 | |||
boolean isAnnotationPresent(Class extends Annotation> ) | X |
此处的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 |
boolean isAnnotationPresent(Class extends Annotation> ) | X | X |
官方的命名规则还是很讲究的:
getDeclaredAnnotationXXXX :只可以获取直接存在注解信息
getXXXXByType : 可以获取间接存在注解信息
getAnnotationXXX : 可以获取继承注解信息
所有的方法都可以获取直接注解信息
isAnnotationPresent 内部调用了 getAnnotation 方法所以该方法可以判断 直接存在和继承直接存在
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return getAnnotation(annotationClass) != null;
}
注:
这里的继承直接存在是指 注解A是可继承的(@Inherit)修饰了某个类C,然后类C的子类D可以取注解信息(注解A)。
上面的例子中, 直接获取注解Role
信息属于是间接存在,如果是获取 注解Roles
信息那么属于直接存在
@Role("role1")
@Role( "role2")
public class Person
@Roles({@Role("role1"),@Role("role2")})
public class Person
上面两种写法是含义是一致的
AnnotatedElement 实现类:
上面的实现类都具有 AnnotatedElement 接口中的方法,但是 注解的继承只针对于 类
才有效。
直接存在
和间接存在
这两种关系,针对于这类元素 getDeclaredAnnotation 和 getAnnotation 等效, getAnnotationsByType和 getDeclaredAnnotationsByType 等效【AccessibleObject.java】
/**
* @throws NullPointerException {@inheritDoc}
* @since 1.8
*/
@Override
public <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {
// Only annotations on classes are inherited, for all other
// objects getDeclaredAnnotation is the same as
// getAnnotation.
return getAnnotation(annotationClass);
}
以上面的Role注解为例,写一个关于继承存在的demo,下面的代码没有什么实际的意义,仅仅为了测试
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Roles {
Role[] value();
}
@Repeatable(Roles.class)
@Inherited
public @interface Role {
String role() default "";
}
定义一个实体类
@Role("role1")
@Role("role2")
@Data
public class Person {
private String name;
}
定义Person的子类
import lombok.Data;
@Data
//@Data是lombok的注解,简化代码,用于生成setter,getter方法
//@Retention(RetentionPolicy.SOURCE)
public class Human extends Person{
private String name;
}
获取Human
对象上的注解信息。 首先它本身是没有注解信息的(@Data 的保存时间仅为 源代码),而且注解Role
使用了@Inherited
修饰,所以使用getDeclaredAnnotation
方法是获取不到注解信息的。同时@Role
注解又使用了@Repeatable
注解。
如果想要获取注解Role的信息,那么有两种方式:
Roles
获取,这种情况下 属于 存在(继承直接存在) 类型,可以通过getAnnotation
(getAnnotations )和getAnnotationsByType
方法获取Role
获取,这种情况下属于 关联(继承间接存在) 类型,只能通过 getAnnotationsByType
方法获取方式一:
@Test
public void testInheritRoles(){
//直接存在
Roles annotationTest=Human.class.getDeclaredAnnotation(Roles.class);
if(annotationTest!=null)
System.out.println("getDeclaredAnnotation\t"+annotationTest.value());
//存在(直接存在,继承直接存在)
Arrays.asList(Human.class.getAnnotations()).forEach(action-> {
System.out.println("getAnnotations \t" + action.toString());
if(action instanceof Roles){
Arrays.stream(((Roles) action).value()).forEach(role -> System.out.println(role.value()));
}
});
//直接存在和间接存在
Arrays.asList(Human.class.getDeclaredAnnotationsByType(Roles.class)).forEach(action-> {
if (action instanceof Roles) {
System.out.println("getDeclaredAnnotationsByType\t"+ action.toString());
}
});
//关联(全部)
Arrays.asList(Human.class.getAnnotationsByType(Roles.class)).forEach(action-> {
System.out.println("getAnnotationsByType\t" + action.toString());
if(action instanceof Roles){
Arrays.stream(((Roles) action).value()).forEach(role -> System.out.println(role.value()));
}
});
}
getAnnotations @annotation.Roles(value=[@annotation.Role(value=role1), @annotation.Role(value=role2)])
role1
role2
getAnnotationsByType @annotation.Roles(value=[@annotation.Role(value=role1), @annotation.Role(value=role2)])
role1
role2
方式二:
@Test
public void testInheritRole(){
//直接存在
Role annotationTest=Human.class.getDeclaredAnnotation(Role.class);
if(annotationTest!=null)
System.out.println("getDeclaredAnnotation\t"+annotationTest.value());
//存在(直接存在,继承直接存在)
Arrays.asList(Human.class.getAnnotations()).forEach(action-> {
if (action instanceof Role) {
System.out.println("getAnnotations\t"+((Role) action).value());
}
});
//关联(全部)
Arrays.asList(Human.class.getAnnotationsByType(Role.class)).forEach(action-> {
if (action instanceof Role) {
System.out.println("getAnnotationsByType\t"+((Role) action).value());
}
});
//直接存在和间接存在
Arrays.asList(Human.class.getDeclaredAnnotationsByType(Role.class)).forEach(action-> {
if (action instanceof Role) {
System.out.println("getDeclaredAnnotationsByType\t"+((Role) action).value());
}
});
}
getAnnotationsByType role1
getAnnotationsByType role2
如果直接获取类Person 上的注解信息 也同样有两种方式
getDeclaredAnnotationsByType
、getAnnotationsByType
方式一:
@Test
public void directAnnotationRoles(){
// 直接存在
Roles roleAnnotation = Person.class.getDeclaredAnnotation(Roles.class);
System.out.println("getDeclaredAnnotations " );
Arrays.stream(roleAnnotation.value()).forEach(role -> System.out.println(role.value()));
//存在(直接存在,继承直接存在)
Arrays.asList(Person.class.getAnnotations()).forEach(action-> {
if (action instanceof Roles) {
System.out.println("getAnnotations\t"+((Role) action).value());
}
});
// 直接存在 或间接存在
Arrays.asList(Person.class.getDeclaredAnnotationsByType(Roles.class)).forEach(action -> {
System.out.println("getDeclaredAnnotationsByType" );
Arrays.stream(action.value()).forEach(role -> System.out.println(role.value()));
});
//关联
Arrays.asList(Person.class.getAnnotationsByType(Role.class)).forEach(action-> {
if (action instanceof Role) {
System.out.println("getAnnotationsByType\t"+((Role) action).value());
}
});
}
getDeclaredAnnotations
role1
role2
getAnnotations
role1
role2
getDeclaredAnnotationsByType
role1
role2
getAnnotationsByType
role1
role2
方式二:
@Test
public void directAnnotationRole(){
//直接存在
Role annotationTest=Person.class.getDeclaredAnnotation(Role.class);
if(annotationTest!=null)
System.out.println("getDeclaredAnnotation\t"+annotationTest.value());
//存在(直接存在,继承直接存在)
Arrays.asList(Person.class.getAnnotations()).forEach(action-> {
if (action instanceof Role) {
System.out.println("getAnnotations\t"+((Role) action).value());
}
});
//直接存在或间接存在
Arrays.asList(Person.class.getDeclaredAnnotationsByType(Role.class)).forEach(action-> {
if (action instanceof Role) {
System.out.println("getDeclaredAnnotationsByType\t"+((Role) action).value());
}
});
//关联
Arrays.asList(Person.class.getAnnotationsByType(Role.class)).forEach(action-> {
if (action instanceof Role) {
System.out.println("getAnnotationsByType\t"+((Role) action).value());
}
});
}
getDeclaredAnnotationsByType role1
getDeclaredAnnotationsByType role2
getAnnotationsByType role1
getAnnotationsByType role2
下面分析一下获取间接存在注解信息的源码
default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) {
Objects.requireNonNull(annotationClass);
//调用 getDeclaredAnnotations() 获取该对象上的存在注解(直接存在注解,继承直接存在注解)
// Arrays.stream() .将获数组转化为 流数据。(jdk1.8新特性)
//Collectors.toMap() 将数组转化为LinkedHashMap ,annotationType(Class extends Annotation>)为 map的key,annotation对象作为value
return AnnotationSupport.
getDirectlyAndIndirectlyPresent(Arrays.stream(getDeclaredAnnotations()).
collect(Collectors.toMap(Annotation::annotationType,
Function.identity(),
((first,second) -> first),
LinkedHashMap::new)),
annotationClass);
}
getDirectlyAndIndirectlyPresent 先获取直接注解,然后获取间接注解
public static <A extends Annotation> A[] getDirectlyAndIndirectlyPresent(Map<Class<? extends Annotation>, Annotation> var0, Class<A> var1) {
// 存放 var1类型的直接存在和间接存在注解信息 的集合
ArrayList var2 = new ArrayList();
//从直接存在注解Map集合中 获取var1类型的注解
Annotation var3 = (Annotation)var0.get(var1);
if (var3 != null) {
// 如果存在 var1 类型的直接注解,添加到结果集合
var2.add(var3);
}
//获取 var1类型的间接存在注解信息(@Repeatable修饰的注解)
Annotation[] var4 = getIndirectlyPresent(var0, var1);
// 存在间接注解
if (var4 != null && var4.length != 0) {
// 去重
boolean var5 = var3 == null || containerBeforeContainee(var0, var1);
var2.addAll(var5 ? 0 : 1, Arrays.asList(var4));
}
Annotation[] var6 = (Annotation[])((Annotation[])Array.newInstance(var1, var2.size()));
return (Annotation[])var2.toArray(var6);
}
getIndirectlyPresent 方法就是检测注解是否使用@Repeatable 修饰,如果存在就解析出这部分数据
private static <A extends Annotation> A[] getIndirectlyPresent(Map<Class<? extends Annotation>, Annotation> var0, Class<A> var1) {
//获取当前注解类型是否使用了Repeatable元注解修饰
//@Repeatable(Roles.class)
//public @interface Role
Repeatable var2 = (Repeatable)var1.getDeclaredAnnotation(Repeatable.class);
if (var2 == null) {
// 注解 var1没有使用注解Repeatable修饰(不是间接存在),那么直接返回null
return null;
} else {
//@Repeatable(Roles.class) 使用@Repeatable元注解中 value属性所表示的注解类型。
//返回 Roles.class
Class var3 = var2.value();
Annotation var4 = (Annotation)var0.get(var3);
if (var4 == null) {
return null;
} else {
// 直接注解中存在 包含重复注解的注解,那么返回该容注解中包含的可重复注解列表
// @Role("role1")
// @Role("role2")
Annotation[] var5 = getValueArray(var4);
checkTypes(var5, var4, var1);
return var5;
}
}
}
按照上面的流程
所以 方法 getDeclaredAnnotationsByType
既可以获取直接存在注解又可以获取间接存在注解
Repeatable var2 = (Repeatable)var1.getDeclaredAnnotation(Repeatable.class);
Class var3 = var2.value();
Annotation var4 = (Annotation)var0.get(var3);
上面的代码也解释了 为什么要必须存在value
属性
问题:
为什么上面判断注解是直接存在注解还是会往下走???
可能存在这个包含可重复注解的元素本身也是可重复出现的。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface RolesArray {
Roles[] value();
}
@Roles({@Role("role1"),@Role("role2")})
@RolesArray({@Roles({@Role("role3")}),@Roles({@Role("role4")})})
public class Person {}
正是因为上面的原因,才有了去重操作
Annotation[] var4 = getIndirectlyPresent(var0, var1);
// 存在间接注解
if (var4 != null && var4.length != 0) {
// containerBeforeContainee 方法去重
boolean var5 = var3 == null || containerBeforeContainee(var0, var1);
var2.addAll(var5 ? 0 : 1, Arrays.asList(var4));
}