Java 6注解处理——从注解获取类

发布于 2024-12-08 10:45:35 字数 1145 浏览 0 评论 0原文

我有一个名为 @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 注释处理的文档和/或教程似乎很少。尝试过谷歌搜索。

I have an custom annotation called @Pojo which I use for automatic wiki documentation generation:

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();
}

I use it like this:

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

to annotation a resource method so that the annotation processor can automatically generate a wiki page describing the resource and type that it expects.

I need to read the value of the value field in an annotation processor, but I am getting a runtime error.

In the source code for my processor I have the following lines:

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

but the actual class in not available to the processor. I think I need a javax.lang.model.type.TypeMirror instead as a surrogate for the real class. I'm not sure how to get one.

The error I am getting is:

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

The Appointment is a class mentioned in one of my @Pojo annotation.

Unfortunately, document and/or tutorials on Java annotation processing seems scarce. Tried googling.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

第七度阳光i 2024-12-15 10:45:35

我来这里是为了问完全相同的问题。 ...并发现相同的 博客链接由拉尔夫发布。

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

这是简单的方法:

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 一开始非常令人困惑,而且冗长得令人费解。我很想推出一个帮助程序类,使所有基本操作变得显而易见......但这是另一天的故事(如果你想要的话,请给我留言)。

I came here to ask the EXACT same question. ... and found the same blog link posted by Ralph.

It's a long article, but very rich. Summary of the story -- there's two ways to do it, the easy way, and the "more right" way.

This is the easy way:

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

The other more tedious way (without exceptions):

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();
    }
}

Of course, once you get a TypeMirror, you (at least in my experience) pretty much always want a TypeElement instead:

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

... that last little non-obvious bit took me an hour of hair pulling before I sorted it out the first time. These annotation processors are actually not that hard to write at all, the API's are just super confusing at first and mindbendingly verbose. I'm tempted to put out a helper class that makes all the basic operations obvious ... but that's a story for another day (msg me if you want it).

御弟哥哥 2024-12-15 10:45:35

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

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

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

Have you read this article: http://blog.retep.org/2009/02/13/getting-class-values-from-annotations-in-an-annotationprocessor/ ?

There the trick is to actually use getAnnotation() and catch the MirroredTypeException. Surprisingly the exception then provides the TypeMirror of the required class.

I don't know if this is a good solution, but it is one. In my personal opinion I would try to get the type behind the MirroredType, but I don't know if this is possible.

那片花海 2024-12-15 10:45:35

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

My answer is basically the same as that from Dave Dopson, only the code was changed a bit:

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;
}

His code used TypeElement as the type for the first argument of getAnnotationMirror(), limiting the use of the method for classes only. Changing that to Element makes it useable for any annotated element.
Using an implementation based on streams instead of the original for loop is just a matter of taste.

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 used Element.getElementValues() to get the annotation values, but this returns only those values that were explicitly applied to the concrete annotation. The defaults will be omitted.
The method Elements.getElementValuesWithDefaults() will return also the defaults (as you may have guessed already from the name of it).

佞臣 2024-12-15 10:45:35

这是我的技巧:

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

This is my trick:

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;
  }

}

Then I can use with any Annotation value:

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

Example

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

Usage in annotation processor

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

Tested with JDK 1.8.0_192

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文