2015-10-07 36 views
10

我嘗試爲Java字節碼實現幾個靜態分析。他們試圖計算某種方法是否具有特定屬性,例如是一種工廠方法。由於這些分析很難測試,因此我決定編寫一些Java代碼並直接使用正確的屬性註釋這些方法。運行分析後,很容易自動檢查計算值和註釋屬性是否相同。Java註釋的默認值是否編譯爲字節碼?

MyAnnotation:

@Retention(RUNTIME) 
@Target(METHOD) 
public @interface FactoryMethodProperty { 

    FactoryMethodKeys value() default FactoryMethodKeys.NonFactoryMethod; 
} 

實例測試代碼:

public class PublicFactoryMethod { 

    private PublicFactoryMethod(){ 
     // I'm private 
    } 

    @FactoryMethodProperty 
    public static void newInstanceAfterOtherConstructorCall(){ 
     new TransFacoryMethod(); 
     new PublicFactoryMethod(); 
    } 

    @FactoryMethodProperty(FactoryMethodKeys.IsFactoryMethod) 
    public static PublicFactoryMethod newInstance(){ 
     return new PublicFactoryMethod(); 
    } 
} 

因爲大部分的在我的測試代碼的方法是沒有工廠的方法,我默認值設置爲枚舉值「FactoryMethodKeys.NonFactoryMethod 」。但是,當我沒有明確地將枚舉值傳遞給註釋時,它不會被編譯爲字節碼。

字節碼:

#23 = Utf8    value 
    #24 = Utf8    Lorg/opalj/fpa/test/annotations/FactoryMethodKeys; 
    #25 = Utf8    IsFactoryMethod 

{ 
    public static void newInstanceAfterOtherConstructorCall(); 
    descriptor:()V 
    flags: ACC_PUBLIC, ACC_STATIC 
    RuntimeVisibleAnnotations: 
     0: #16() 
    Code: 
     stack=1, locals=0, args_size=0 
     0: new   #17     // class factoryMethodTest/TransFacoryMethod 
     3: invokespecial #19     // Method factoryMethodTest/TransFacoryMethod."<init>":()V 
     6: new   #1     // class factoryMethodTest/PublicFactoryMethod 
     9: invokespecial #20     // Method "<init>":()V 
     12: return 
     LineNumberTable: 
     line 49: 0 
     line 50: 6 
     line 51: 12 
     LocalVariableTable: 
     Start Length Slot Name Signature 

    public static factoryMethodTest.PublicFactoryMethod newInstance(); 
    descriptor:()LfactoryMethodTest/PublicFactoryMethod; 
    flags: ACC_PUBLIC, ACC_STATIC 
    RuntimeVisibleAnnotations: 
     0: #16(#23=e#24.#25) 
    Code: 
     stack=2, locals=0, args_size=0 
     0: new   #1     // class factoryMethodTest/PublicFactoryMethod 
     3: dup 
     4: invokespecial #20     // Method "<init>":()V 
     7: areturn 
     LineNumberTable: 
     line 55: 0 
     LocalVariableTable: 
     Start Length Slot Name Signature 
} 

我是怎麼了?爲什麼默認值完全被忽略?

回答

8

它不需要在那裏。在運行時,JVM構造一個可以檢索的註釋實例。此實例將使用.class文件中的default值進行初始化,以獲取註釋本身。這表示爲AnnotationDefault attribute

AnnotationDefault屬性是在特定method_info結構(§4.6),即那些 表示註釋類型的元素的屬性 表的可變長度屬性。屬性記錄 method_info結構所表示的元素的默認值。

每個method_info代表註釋 類型的元素的結構可以包含至多一個AnnotationDefault屬性。 Java 虛擬機必須使此默認值可用,因此它可以是 由適當的反射API應用。

您最終將調用註解實例的value()方法(或您定義的任何其他方法),並且它將返回該值。

4

如果您查看註釋的字節代碼,您會在那裏看到默認值。使用javap -c -v並修剪不相關的東西:

... 
ConstantPool: 
    #7 = Utf8    LFactoryMethodKeys 
    #8 = Utf8    NonFactoryMethod 
... 
{ 
    public abstract FactoryMethodKeys value(); 
    flags: ACC_PUBLIC, ACC_ABSTRACT 
    AnnotationDefault: 
     default_value: e#7.#8}