Java 注解的默认值是否被编译成字节码?

Are Java annotation's default values compiled into bytecode?

我尝试对 Java 字节码进行一些静态分析。他们尝试计算某种方法是否具有特定的 属性,例如是一个工厂方法。因为这些分析很难测试,所以我决定写一些Java代码,直接用正确的属性注释方法。 运行 分析后,很容易自动检查计算的和注释的 属性 是否相同。

我的注释:

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

我做错了什么?为什么默认值被完全忽略了?

它不需要在那里。在运行时,JVM 构造一个您可以检索的注释实例。此实例将使用注释本身的 .class 文件中的 default 值进行初始化。这表示为 AnnotationDefault attribute

The AnnotationDefault attribute is a variable-length attribute in the attributes table of certain method_info structures (§4.6), namely those representing elements of annotation types. TheAnnotationDefault attribute records the default value for the element represented by the method_info structure.

Each method_info structure representing an element of an annotation type may contain at most one AnnotationDefault attribute. The Java Virtual Machine must make this default value available so it can be applied by appropriate reflective APIs.

您最终将调用注释实例的 value() 方法(或您定义的任何其他方法),它将 return 该值。

如果您查看 注释 的字节码,您会在那里看到默认值。使用 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}