Значения по умолчанию аннотации Java, скомпилированные в байт-код?
Я пытаюсь реализовать несколько статических анализов для байт-кода Java. Они пытаются вычислить, имеет ли определенный метод определенное свойство, например. является методом factory. Поскольку эти анализы трудно проверить, я решил написать некоторый 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();
}
}
Поскольку большинство методов в моем тестовом коде не являются методами factory, я устанавливал значение по умолчанию для значения перечисления "FactoryMethodKeys.NonFactoryMethod". Но когда я явно не передаю значение enum в аннотацию, он не компилируется в байт-код.
Bytecode:
#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
}
Что я не так понял? Почему значение по умолчанию полностью игнорируется?
Ответы
Ответ 1
Этого не должно быть. Во время выполнения JVM создает экземпляр аннотации, который вы можете получить. Этот экземпляр будет инициализирован значением default
, которое находится в файле .class
для самой аннотации. Это представлено как атрибут AnnotationDefault
Атрибут AnnotationDefault
является атрибутом переменной длины в таблицы атрибутов определенных структур method_info
(§4.6), а именно: те , представляющие элементы типов аннотаций. AnnotationDefault
атрибут записывает значение по умолчанию для элемента, представленного method_info
.
Каждая структура method_info
, представляющая элемент аннотации тип может содержать не более одного атрибута AnnotationDefault
. Java Виртуальная машина должна сделать это значение по умолчанию доступным, чтобы оно могло быть применяемые соответствующими отражающими API.
В конечном итоге вы вызовете метод экземпляра аннотации value()
(или какой-либо другой метод, который вы определили), и он вернет это значение.
Ответ 2
Если вы посмотрите на байт-код для аннотации, вы увидите там по умолчанию. Используя 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}