如何使用 Java8 获取 VariableElement 的类型注释和属性值?

doa*_*hai 5 java annotation-processing

考虑以下代码:

public class SimpleTest {

    private Map<@JSON Integer,Map<@Frozen Integer,@Enumerated(value = Enumerated.Encoding.NAME, test = "123") String>> map;
}
Run Code Online (Sandbox Code Playgroud)

使用最新的 JDK8 API 用于注释处理,如何从 VariableElement访问注释列表(@JSON@Frozen@Enumerated)及其相应的属性(@Enumerated的值和测试)?

final VariableElement mapElm = els.stream().filter(x -> x.getSimpleName().contentEquals("map")).findFirst().get();
???
???
Run Code Online (Sandbox Code Playgroud)

我尝试了很多技巧,例如mapElm.getTypeArguments().get(0)@Json Integer我从未成功获得注释@JSON ...

编辑:通过访问 JDK 的内部类,我可以访问这些注释,但它对 impl 更改非常hacky 和敏感,我想知道是否有更好的方法

public static class SimpleEntityCodecFactoryTest {

    private Map<@JSON Integer,Map<@Frozen Integer,@Enumerated(value = Enumerated.Encoding.NAME, test = "123") String>> map;
}

final TypeElement typeElement = elementUtils.getTypeElement(SimpleEntityCodecFactoryTest.class.getCanonicalName());
final List<VariableElement> els = ElementFilter.fieldsIn(typeElement.getEnclosedElements());

final VariableElement mapElt = els.stream().filter(x -> x.getSimpleName().contentEquals("map")).findFirst().get();
final com.sun.tools.javac.util.List<Attribute.TypeCompound> typeAttributes = ((Symbol.VarSymbol) mapElt).getMetadata().getTypeAttributes();
for (Attribute.TypeCompound typeAttribute : typeAttributes) {
    final DeclaredType annotationType = typeAttribute.getAnnotationType();
    System.out.println(format("Accessing annotation '%s' at location : %s",annotationType.toString(),typeAttribute.getPosition().location));
    for (Map.Entry<Symbol.MethodSymbol,Attribute> entry : typeAttribute.getElementValues().entrySet()) {
        final Symbol.MethodSymbol methodSymbol = entry.getKey();
        final Attribute attribute = entry.getValue();
        System.out.println(format("Attribute '%s' for annotation '%s' : %s", methodSymbol.name, annotationType.toString(), attribute.toString()));
    }
}    
Run Code Online (Sandbox Code Playgroud)

输出显示:

Accessing annotation 'info.archinnov.achilles.annotations.JSON' at location : TYPE_ARGUMENT(0)
Accessing annotation 'info.archinnov.achilles.annotations.Frozen' at location : TYPE_ARGUMENT(1),TYPE_ARGUMENT(0)
Accessing annotation 'info.archinnov.achilles.annotations.Enumerated' at location : TYPE_ARGUMENT(1),TYPE_ARGUMENT(1)
Attribute 'value' for annotation 'info.archinnov.achilles.annotations.Enumerated' : info.archinnov.achilles.annotations.Enumerated.Encoding.NAME
Attribute 'test' for annotation 'info.archinnov.achilles.annotations.Enumerated' : "123"
Run Code Online (Sandbox Code Playgroud)

上面的代码在IntelliJ中工作正常,但由于脏转换((Symbol.VarSymbol) mapElt).getMetadata(),它可以与Oracle JDK一起工作,但与Eclipse 编译器一起失败。

现在,除了脏转换之外,我没有找到任何其他解决方案来访问泛型类型中的注释。任何想法都受到欢迎

解决方案:

感谢 Werner ( wmdietl ),我可以使用Tree API而不是ElementsTypeMirror访问嵌套注释

然而我很困惑,因为一旦我到达那里,就不可能将Tree的任何子类转换回ElementTypeMirror(我的真正目标)。

我所有的注释处理都大量使用 JavaPoet ( https://github.com/square/javapoet ) 来生成干净的源代码,并且该框架仅处理 TypeMirror,而不处理 Tree

https://github.com/typetools/checker-framework/blob/master/javacutil/src/org/checkerframework/javacutil/TreeUtils.java类中,有一些方法将Tree转换回Element但它依赖于InternalUtils ,我无法使用它,因为它与Eclipse ECJ 编译器不兼容。

我想我必须等待 JDK 9 才能拥有与 ECJ 编译器兼容的可用 Element API

编辑:为了使类型注释适用于Eclipse Compiler,我必须强制转换为内部编译器类,如下所示: https: //github.com/doanduyhai/Achilles/blob/master/achilles-core/src/main/java/info /archinnov/achilles/internals/parser/AnnotationTree.java#L83-L85。这很丑陋,但这是目前 JDK9 之前唯一的方法。

syn*_*mat -3

@JSON 必须用 @Retention(RetentionPolicy.RUNTIME) 修饰,以便可以使用反射。

AnnotatedParameterizedType mapField = (AnnotatedParameterizedType)SimpleTest.class.getDeclaredFields()[0].getAnnotatedType();
AnnotatedType integerType = mapField.getAnnotatedActualTypeArguments()[0];

System.out.println(integerType.getAnnotations()[0]);
// -> @JSON()
Run Code Online (Sandbox Code Playgroud)

请参阅注释参数化类型

  • 反射 API 对于运行时自省很有用。我想在编译时使用 **注释处理** API。 (2认同)