56

我有一个名为 @Pojo 的自定义注释,用于自动生成 wiki 文档:

package com.example.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.METHOD)
public @interface Pojo {
    Class<?> value();
}

我这样使用它:

@Pojo(com.example.restserver.model.appointment.Appointment.class)

注释资源方法,以便注释处理器可以自动生成描述其期望的资源和类型的 wiki 页面。

我需要value在注释处理器中读取字段的值,但出现运行时错误。

在我的处理器的源代码中,我有以下几行:

final Pojo pojo = element.getAnnotation(Pojo.class);
// ...
final Class<?> pojoJavaClass = pojo.value();

但处理器无法使用实际的类。我想我需要一个javax.lang.model.type.TypeMirror代替真实班级的代理人。我不知道如何得到一个。

我得到的错误是:

javax.lang.model.type.MirroredTypeException: Attempt to access Class object for TypeMirror com.example.restserver.model.appointment.Appointment

这是我的一个注释Appointment中提到的一个类。@Pojo

不幸的是,关于 Java 注释处理的文档和/或教程似乎很少。尝试谷歌搜索。

4

4 回答 4

53

我来这里是为了问完全相同的问题。...并找到了Ralph 发布的相同博客链接。

文章很长,但是内容很丰富。故事总结——有两种方法可以做到,简单的方法和“更正确”的方法。

这是简单的方法:

private static TypeMirror getMyValue1(MyAnnotation annotation) {
    try
    {
        annotation.myValue(); // this should throw
    }
    catch( MirroredTypeException mte )
    {
        return mte.getTypeMirror();
    }
    return null; // can this ever happen ??
}

另一种更繁琐的方式(无一例外):

private static AnnotationMirror getAnnotationMirror(TypeElement typeElement, Class<?> clazz) {
    String clazzName = clazz.getName();
    for(AnnotationMirror m : typeElement.getAnnotationMirrors()) {
        if(m.getAnnotationType().toString().equals(clazzName)) {
            return m;
        }
    }
    return null;
}

private static AnnotationValue getAnnotationValue(AnnotationMirror annotationMirror, String key) {
    for(Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotationMirror.getElementValues().entrySet() ) {
        if(entry.getKey().getSimpleName().toString().equals(key)) {
            return entry.getValue();
        }
    }
    return null;
}


public TypeMirror getMyValue2(TypeElement foo) {
    AnnotationMirror am = getAnnotationMirror(foo, MyAnnotation.class);
    if(am == null) {
        return null;
    }
    AnnotationValue av = getAnnotationValue(am, "myValue");
    if(av == null) {
        return null;
    } else {
        return (TypeMirror)av.getValue();
    }
}

当然,一旦你获得了 TypeMirror,你(至少根据我的经验)几乎总是想要一个 TypeElement:

private TypeElement asTypeElement(TypeMirror typeMirror) {
    Types TypeUtils = this.processingEnv.getTypeUtils();
    return (TypeElement)TypeUtils.asElement(typeMirror);
}

...在我第一次整理出来之前,最后一点不明显的部分花了我一个小时的时间拉头发。这些注释处理器实际上一点也不难编写,API 起初只是超级混乱而且冗长得令人费解。我很想推出一个帮助类,使所有基本操作都很明显......但这是另一天的故事(如果你想要的话,给我发消息)。

于 2012-04-16T00:56:27.197 回答
31

你读过这篇文章吗:http: //blog.retep.org/2009/02/13/getting-class-values-from-annotations-in-an-annotationprocessor/

诀窍是实际使用 getAnnotation() 并捕获 MirroredTypeException。令人惊讶的是,异常随后提供了所需类的 TypeMirror。

我不知道这是否是一个好的解决方案,但它是一个。在我个人看来,我会尝试获取 MirroredType 背后的类型,但我不知道这是否可能。

于 2011-10-07T13:35:03.050 回答
2

我的回答与 Dave Dopson 的回答基本相同,只是代码稍作改动:

public default Optional<? extends AnnotationMirror> getAnnotationMirror( final Element element, final Class<? extends Annotation> annotationClass )
{
    final var annotationClassName = annotationClass.getName();
    final Optional<? extends AnnotationMirror> retValue = element.getAnnotationMirrors().stream()
        .filter( m -> m.getAnnotationType().toString().equals( annotationClassName ) )
        .findFirst();

    return retValue;
}

他的代码用作TypeElement的第一个参数的类型getAnnotationMirror(),限制了该方法仅用于类。将其更改为Element可用于任何带注释的元素。使用基于流而不是原始for循环的实现只是个人喜好问题。

public default Optional<? extends AnnotationValue> getAnnotationValue( final AnnotationMirror annotationMirror, final String name )
{
    final Elements elementUtils = this.processingEnv.getElementUtils();
    final Map<? extends ExecutableElement, ? extends AnnotationValue> elementValues = elementUtils.getElementValuesWithDefaults( annotationMirror );
    final Optional<? extends AnnotationValue> retValue = elementValues.keySet().stream()
        .filter( k -> k.getSimpleName().toString().equals( name ) )
        .map( k -> elementValues.get( k ) )
        .findAny();

    return retValue;
}

Dave 用于Element.getElementValues()获取注释值,但这仅返回那些显式应用于具体注释的值。默认值将被省略。该方法Elements.getElementValuesWithDefaults()还将返回默认值(您可能已经从它的名称中猜到了)。

于 2018-09-10T12:31:44.207 回答
1

这是我的把戏:

public class APUtils {

  @FunctionalInterface
  public interface GetClassValue {
    void execute() throws MirroredTypeException, MirroredTypesException;
  }

  public static List<? extends TypeMirror> getTypeMirrorFromAnnotationValue(GetClassValue c) {
    try {
      c.execute();
    }
    catch(MirroredTypesException ex) {
      return ex.getTypeMirrors();
    }
    return null;
  }

}

然后我可以使用任何注释值:

public @interface MyAnnotationType {
  public Class<?>[] value() default {};
}

例子

@MyAnnotationType(String.class)
public class Test { ... }

注释处理器中的用法

MyAnnotationType a = element.getAnnotation(MyAnnotationType.class);
List<? extends TypeMirror> types = APUtils.getTypeMirrorFromAnnotationValue(() -> a.value());

使用 JDK 1.8.0_192 测试

于 2018-10-13T14:15:11.090 回答