欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java 获取注解信息的方法

程序员文章站 2023-11-05 12:39:46
文章目录基础一、官方提供的注解1.@Retention2.@Target二、自定义注解获取注解获取注解信息一 、直接存在二、间接存在三、存在四、关联总结demo1.直接存在间接存在关联基础一、官方提供的注解jdk 内置了一些注解基本注解java.lang@Override@Deprecated@SuppressWarnings元注解 :用来修饰其他注解的注解。jav......

如果是为了当查询手册可以直接跳到 总结

基础

一、官方提供的注解

jdk 内置了一些注解
基本注解

  1. java.lang
    • @Override
    • @Deprecated
    • @SuppressWarnings
    • @FunctionalInterface (jdk 1.8)
    • @SafeVarargs

元注解 :用来修饰其他注解的注解。

  1. java.lang.annotation
    • @Retention
    • @Target
    • @Documented
    • @Inherited
    • @Repeatable : 1.8

工作中比较常用的就是元注解就是 @Retention 和 @Target。下面简单介绍我们接下来会用到的

1.@Retention
  • 只能修饰注解。 【@Target(ElementType.ANNOTATION_TYPE)】
  • 只包含一个成员 value类型为【RetentionPolicy】。 来控制某个注解可以保留多长时间

RetentionPolicy 有以下三个值:

  • RetentionPolicy.SOURCE:注解信息只保存在源文件中(java),编译时会丢失
  • RetentionPolicy.CLASS:默认的行为。 源文件编译后注解信息保存在class文件中,但是虚拟机运行时不会保存该信息。
  • RetentionPolicy.RUNTIME: 注解信息不仅保留在class文件中,还是保存在JVM中,所以运行时可以通过反射来获取注解信息。
2.@Target
  • 只能修饰注解。 【@Target(ElementType.ANNOTATION_TYPE)】
  • 只包含一个成员 value类型为【ElementType[]】。指定被修饰的注解可以修饰哪些元素

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();
}
3.@Inherited

@Inherited 所修饰的注解 将具有继承性。例如B继承了A,A添加了注解C(该注解被@Inherited修饰),那么B将会具有A注解

4.@Repeatable

注解通常只能直接修饰某个元素一次,如果修饰了多次那么会出现编译异常

public @interface Role {
    String role() default "";
}

java 获取注解信息的方法

如果想要某个注解在某个元素上(直接)出现多次,那么就可以使用@Repeatable 注解:

  1. 只能修饰注解。 【@Target(ElementType.ANNOTATION_TYPE)
  2. 存在 value 成员,表示包含可重复注解的注解类型 【Class<? extends Annotation> value()
  3. value 属性值表示的注解必须存在value成员,这个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接口。

实现类:

  • Class:类定义
  • Constructor: 构造器
  • Field: 属性定义
  • Method: 方法定义
  • Package :包定义
  • Parameter: 参数定义

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 接口提供了多种方法来获取不同定义方式下的注解。(不知道怎么翻译比较好,就直译了,或许 “修饰”、“呈现” 比较好些)

  • 直接存在(directly present): 注解直接修饰在某个元素(类,属性,方法,注解)上。
  • 间接存在: 注解使用@Repeatable修饰,并且该注解直接修饰在元素上(类,属性,方法,注解)
  • 存在: 直接存在,或者继承存在( 被@Inherited 修饰)
  • 关联: @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");
    }
}
  1. 首先编译java文件

    javac -d . Name.java
    javac -d . Person1.java
    然后使用javap 命令查看class文件

  2. 查看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."<init>":()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          // "<init>":()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."<init>":()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 <em>directly present</em> on this element.
         * This method ignores inherited annotations.
         *
         * If there are no annotations <em>directly present</em> 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 <em>directly present</em>, 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.

总结下来就是:

  • 注解必须使用元注解 @Repeatable 修饰
  • 该注解直接修饰元素

同样的间接存在 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)])

注意:
就上面的事例而言:

  • @Roles 属于直接存在注解
  • @Role 属于间接存在注解

获取该注解方法:

/**
     * Returns this element's annotation(s) for the specified type if
     * such annotations are either <em>directly present</em> or
     * <em>indirectly present</em>. 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 <em>repeatable annotation type</em> (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.
     *
     * <p>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 <T> 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:

  1. A is directly present on E; or
  2. 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.

以下两种情况都称之为存在:

  1. 注解直接修饰某个元素E上。 (直接存在)
  2. 注解是可继承(@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 <em>present</em>, else null.
     *
     * @param <T> 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 <em>present</em> on this element.
     *
     * If there are no annotations <em>present</em> 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 <em>present</em> on this element, else false.  This method
     * is designed primarily for convenient access to marker annotations.
     *
     * <p>The truth value returned by this method is equivalent to:
     * {@code getAnnotation(annotationClass) != null}
     *
     * <p>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;
    }
四、关联

以下两种情况都称之为关联:

  1. A is directly or indirectly present on E 。直接或者间接存在
  2. No annotation of A 's type is directly or indirectly present on E, and E is a class, and A’s type is inheritable, and A is associated with the superclass of E。 如果注解既不是直接存在或者间接存在(没有直接修饰),并且注解是可继承,并且和所修饰类的父类关联。简单的说就是当前类的父类(超类)最终总会和注解存在一个直接存在或者间接存在的关系。

获取关联关系注解的方法

 /**
     * Returns annotations that are <em>associated</em> with this element.
     *
     * If there are no annotations <em>associated</em> 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 <em>repeatable
     * annotation type</em> (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 <T> 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<T>) X
Annotation[] getDeclaredAnnotations() X
T[] getDeclaredAnnotationsByType(Class<T>) X X
T getAnnotation(Class<T>) X
Annotation[] getAnnotations() X
T[] getAnnotationsByType(Class<T>) X
boolean isAnnotationPresent(Class<? extends Annotation> ) X

此处的X 和我们认知的√ 含义是相同的。

虽然有四种关系,实际上都是基于两种关系(直接存在,间接存在)推演的。

Method Directly Present Indirectly Present inherit Directly Present inherit Indirectly Present
T getDeclaredAnnotation(Class<T>) X
Annotation[] getDeclaredAnnotations() X
T[] getDeclaredAnnotationsByType(Class<T>) X X
T getAnnotation(Class<T>) X X
Annotation[] getAnnotations() X X
T[] getAnnotationsByType(Class<T>) 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 实现类:

  • Class:类定义
  • Constructor: 构造器
  • Field: 属性定义
  • Method: 方法定义
  • Package :包定义
  • Parameter: 参数定义

上面的实现类都具有 AnnotatedElement 接口中的方法,但是 注解的继承只针对于 才有效。

  • 所以只有Class对象 具有上面四种 注解定义关系
  • 其他元素 Filed、Method 只有直接存在间接存在 这两种关系,针对于这类元素 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);
    }
demo

以上面的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上的注解信息

获取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上的注解信息

如果直接获取类Person 上的注解信息 也同样有两种方式

  • 获取 Roles 注解。这种情况下 Roles注解明显属于 直接存在,所以这些方法都可以获取注解信息
  • 获取Role注解。Role属于 间接存在,所以只有两个方法可以获取注解信息,getDeclaredAnnotationsByTypegetAnnotationsByType

方式一:

	@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

getDeclaredAnnotationsByType 分析

下面分析一下获取间接存在注解信息的源码

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

按照上面的流程

  • 首先判断该注解是否是 直接存在注解,如果是那么直接添加结果集合中;
  • 之后判断该注解是否是间接存在注解(@Repeatable修饰),如果是那么返回包含该重复注解(@Role)的容器注解(@Roles)信息;
  • 如果这个容器注解是该元素(类、方法、、属性等)的直接存在注解,那么就可以返回该容器注解包含的信息(Role[] value)
  • 判断间接存在注解 是否存在,如果间接注解存在 那么进行去重处理。去重的原因下面会提及
  • 返回结果,如果不存在这样的直接或间接注解那么返回空数组

所以 方法 getDeclaredAnnotationsByType 既可以获取直接存在注解又可以获取间接存在注解

回到最初的美好,↑
 Repeatable var2 = (Repeatable)var1.getDeclaredAnnotation(Repeatable.class);
 Class var3 = var2.value();
 Annotation var4 = (Annotation)var0.get(var3);

上面的代码也解释了 为什么要必须存在value 属性

问题:

  1. 为什么上面判断注解是直接存在注解还是会往下走???
    可能存在这个包含可重复注解的元素本身也是可重复出现的。

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

本文地址:https://blog.csdn.net/yamadeee/article/details/96479009