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}
我尝试对 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 certainmethod_info
structures (§4.6), namely those representing elements of annotation types. TheAnnotationDefault
attribute records the default value for the element represented by themethod_info
structure.Each
method_info
structure representing an element of an annotation type may contain at most oneAnnotationDefault
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}