@Repeatable詳解-getAnnotation、getDeclaredAnnotation獲取不到對象

1 @Repeatable

     jdk8之前不存在可重複註解,即一個註解不能出現在同一個元素(Class、Method、Constructor、Field)2次以上。下面的寫法是被禁止的:

// @ComponentScan重複使用
@ComponentScan(basePackages = "com.focuse.component1")
@ComponentScan(basePackages = "com.focuse.component2")
public class RepeatableTest {

}

如果想添加多個相同註解,只能用容器註解。容器註解提供了一個數組,來承載多個註解

@ComponentScans(value = { @ComponentScan(basePackages = "com.focuse.component1"),
        @ComponentScan(basePackages = "com.focuse.component2") })
public class RepeatableTest {
    
}

jdk8引入了@Repeatable元註解。被@Repeatable修飾的註解即是可重複使用在同一個元素上的。在jdk8中@ComponentScan就被@Repeatable修飾了,這樣第一種寫法在jdk8中被允許的。

2 get[Declared]Annotation、get[Declared]Annotations、get[Declared]AnnotationsByType

    java的反射包中獲取一個元素(Class、 Method、Constructor、Field)上的註解對象主要有這6個方法getAnnotation(Class<T> annotationClass)、getAnnotations()、getAnnotationsByType(Class<T> annotationClass)、getDeclaredAnnotation(Class<T> annotationClass)、getDeclaredAnnotations()、getDeclaredAnnotationsByType(Class<T> annotationClass)。這些方法獲取的都是運行時註解。

    在介紹這6個方法前,先描述下幾個概念(翻譯自jdk的註釋):

  • directly present:"直接修飾"註解是指元素的RuntimeVisibleAnnotations、RuntimeVisibleParameterAnnotations或RuntimeVisibleTypeAnnotations屬性包含的註解(RuntimeVisibleAnnotations、RuntimeVisibleParameterAnnotations、RuntimeVisibleTypeAnnotations是classfile結構的屬性),
    說白了就是指直接修飾在某個元素上的註解;
  • indirectly present:"間接修飾"註解就是指得容器註解的數組中指定的註解;
  • present:並不是"直接修飾"註解和"間接修飾"註解的合集,而是"直接修飾"註解和父類繼承下來的"直接註解"的合集;
  • associated:"關聯"是"直接修飾"註解、"間接修飾"註解以及父類繼承下來的註解的合集;

理解了這幾個概念後,以上6個方法返回的註解對象可用下表來表示

方法 directly present indirectly present present associated
getAnnotation     *  
getAnnotations     *  
getAnnotationsByType       *
getDeclaredAnnotation *      
getDeclaredAnnotations *      
getDeclaredAnnotationsByType * *    

舉個例子getAnnotation返回的是"直接修飾"註解和繼承的註解的合集,不包括容器註解裏包含的註解;而getDeclaredAnnotation僅僅返回"直接修飾"註解。

3 @Repeatable對上述方法返回值的影響

    如果一個註解是可重複的,當它在同一個元素E上出現2次以上時(注意出現1次的時候並不會默認生成容器註解),javac編譯以後,class文件裏面實際是該註解對應的容器註解修飾在元素E上,而容器註解中的value存儲多個註解的值。舉個例子:

a 自定義可重複的註解@RepeatAn

   @RepeatAn的容器是@RepeatAns

package com.focuse.jdkdemo.annation;

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

import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Target(ElementType.METHOD)
@Retention(RUNTIME)
@Repeatable(RepeatAns.class)
public @interface RepeatAn {
}

b 定義容器註解@RepeatAns

package com.focuse.jdkdemo.annation;

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

import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Target(ElementType.METHOD)
@Retention(RUNTIME)
public @interface RepeatAns {
    RepeatAn[] value();
}

c 使用註解@RepeatAn

package com.focuse.jdkdemo.annation;

import java.lang.reflect.Method;

/**
 * @author :focuse
 * @date :Created in 2020/2/16 上午11:10
 * @description:
 * @modified By:
 */
public class RepeatableTest<T extends  String> {

    @RepeatAn()
    @RepeatAn()
    public void method(String s) {

    }

    public static void main(String[] args) throws Exception{
        Method method = RepeatableTest.class.getDeclaredMethod("method", String.class);
        RepeatAn an = method.getAnnotation(RepeatAn.class);
        System.out.println(an);
        System.out.println("****************************************");
        RepeatAns ans = method.getAnnotation(RepeatAns.class);
        System.out.println(ans);
        System.out.println("****************************************");
        RepeatAn[] anArr = method.getAnnotationsByType(RepeatAn.class);
        System.out.println(anArr);

    }
}

運行結果

因爲可重複註解出現2次以上時,編譯以後相當於

@RepeatAns(value = {@RepeatAn(), @RepeatAn()})

所以: 

  • getAnnotation獲取的是"直接修飾"註解和繼承下來的註解,這其中沒有@RepeatAn所以爲null;
  • 同理,getAnnotation(RepeatAns.class)卻可以獲取到;
  • getAnnotationsByType(RepeatAn.class)獲取的是"關聯"註解(包括"間接"註解),所以獲取@RepeatAn的數組;

d 編譯之後的classfile文件

    最後我們來看看編譯之後的classfile文件到底是什麼樣的,用命令"javap -c -v -l RepeatableTest.class"反編譯class文件得到如下信息

"method"方法最後有個屬性RuntimeVisibleAnnotations就是存放註解的,#24、#25、#26是類文件常量池中索引,具體含義如下 

 可以看到#24對應Lcom/focuse/jdkdemo/annation/RepeatAns即@RepeatAns,#25對應"value"字符串,#26對應@RepeatAn。n那麼RuntimeVisibleAnnotations中信息組合起來就是@RepeatAns(value=[@RepeatAn,@RepeatAn]),與例子中現象一致。

PS:注意可重複註解在同一個元素上只出現1次的時候並不會默認生成容器註解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章