Почему Javassist настаивает на поиске значения аннотации по умолчанию, если оно указано явно?

Я использую Javassist для добавления и изменения аннотаций в package-info "классе".

В некоторых случаях мне нужно иметь дело со следующим пограничным случаем. Кто-то (неправильно) указал аннотацию @XmlJavaTypeAdapters в пакете package-info, но не предоставил атрибут value (который имеет вид определены как обязательные). Так это выглядит так:

@XmlJavaTypeAdapters // XXX incorrect; value() is required, but javac has no problem
package com.foobar;

import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;

В Javassist это происходит немного странно.

javassist.bytecode.annotation.Annotation, представляющий аннотацию @XmlJavaTypeAdapters, не имеет значения члена (getMemberValue("value") возвращает null), как и ожидалось.

Конечно, можно добавить значение члена value(), что я и сделал:

if (adaptersAnnotation.getMemberValue("value") == null) {
    final ArrayMemberValue amv = new ArrayMemberValue(new AnnotationMemberValue(constantPool), constantPool);
    adaptersAnnotation.addMemberValue("value", amv);
    annotationsAttribute.addAnnotation(adaptersAnnotation);
}

В приведенном выше фрагменте кода я создал новое значение члена для хранения массива аннотаций, поскольку атрибут value() элемента @XmlJavaTypeAdapters представляет собой массив @XmlJavaTypeAdapter. Я указал его тип массива, пытаясь угадать намерение документации, похожей на Zen. Кажется, что если вы укажете еще один MemberValue, этот MemberValue каким-то образом будет служить типом массива. В моем случае я хочу, чтобы тип массива был @XmlJavaTypeAdapter, который является аннотацией, поэтому единственным типом MemberValue, который мне показался подходящим, был AnnotationMemberValue. Поэтому я создал пустой из них и установил его как тип массива.

Это прекрасно работает до тех пор, пока вы остаетесь «внутри» Javassist.

Однако, похоже, что-то пошло не так. Если я попрошу Javassist преобразовать все свои проприетарные аннотации в настоящие Java java.lang.annotation.Annotation, то, когда я попытаюсь получить доступ к атрибуту value() этой аннотации @XmlJavaTypeAdapters, Javassist скажет мне, что значения по умолчанию нет. Хм?

Другими словами, это нормально, на самом деле это не так, но я указал то, что, как я надеялся, было массивом нулевой длины (то есть значение по умолчанию не должно использоваться; вместо этого следует использовать мой явно указанный массив нулевой длины):

final List<Object> annotations = java.util.Arrays.asList(packageInfoClass.getAnnotations());
for (final Object a : annotations) {
  System.out.println("*** class annotation: " + a); // OK; one of these is @XmlJavaTypeAdapters
  System.out.println("    ...of type: " + a.getClass()); // OK; resolves to XmlJavaTypeAdapters
  System.out.println("    ...assignable to java.lang.annotation.Annotation? " + java.lang.annotation.Annotation.class.isInstance(a)); // OK; returns true

  if (a instanceof XmlJavaTypeAdapters) {
    final XmlJavaTypeAdapters x = (XmlJavaTypeAdapters)a;
    System.out.println("    ...value: " + java.util.Arrays.asList(x.value())); // XXX x.value() throws an exception
  }
}

Так почему же в этом случае Javassist ищет значение по умолчанию?

Моя большая проблема, конечно, заключается в том, чтобы справиться с этим (к сожалению, довольно распространенным) случаем, когда @XmlJavaTypeAdapters указано без дополнительной информации о нем. Мне нужно добавить значение члена value, которое может содержать массив аннотаций @XmlJavaTypeAdapter. Я не могу понять, как это сделать с помощью Javassist. Как всегда, вся помощь приветствуется.


person Laird Nelson    schedule 31.12.2011    source источник


Ответы (1)


Для потомков кажется, что в этом конкретном случае (чтобы избежать NullPointerException и/или RuntimeException) вам нужно сделать это:

if (adaptersAnnotation.getMemberValue("value") == null) {
    final ArrayMemberValue amv = new ArrayMemberValue(constantPool);
    amv.setValue(new AnnotationMemberValue[0]);
    adaptersAnnotation.addMemberValue("value", amv);
    annotationsAttribute.addAnnotation(adaptersAnnotation);
}

В частности, обратите внимание, что я намеренно опускаю тип массива при построении ArrayMemberValue (включение любого типа приведет к исключению). Затем я явно устанавливаю его значение в пустой массив типа AnnotationMemberValue. Любая другая комбинация здесь приведет к исключению.

Кроме того, что очень странно, последняя строка в этом блоке if является критической. Несмотря на то, что в этом конкретном случае сама аннотация была найдена и, следовательно, уже присутствовала в AnnotationsAttribute, вы должны добавить ее повторно. Если вы этого не сделаете, вы получите RuntimeException жалобу на отсутствие значения по умолчанию.

Я надеюсь, что это поможет другим Javassist хакерам.

person Laird Nelson    schedule 31.12.2011