Java 6注释处理 - 从注释中获取类

时间:2011-10-07 13:19:01

标签: java types annotation-processing

我有一个名为@Pojo的自定义注释,我用它来生成自动维基文档:

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 个答案:

答案 0 :(得分:40)

我来这里问同样的问题。 ......并找到了拉尔夫发布的blog link

这是一篇很长的文章,但非常丰富。故事摘要 - 有两种方法可以做到,简单方法和“更正确”的方式。

这是一种简单的方法:

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最初只是令人困惑,而且令人费解。我很想推出一个让所有基本操作显而易见的辅助类......但那是另一天的故事(如果你需要的话,请告诉我)。

答案 1 :(得分:24)

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

  

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

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

答案 2 :(得分:1)

我的答案与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()也将返回默认值(您可能已经从它的名称中猜到了)。

答案 3 :(得分:0)

这是我的把戏:

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测试