Java 6注解处理——从注解中获取一个类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7687829/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java 6 annotation processing -- getting a class from an annotation
提问by Ralph
I have an custom annotation called @Pojo which I use for automatic wiki documentation generation:
我有一个名为 @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();
}
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.
注释资源方法,以便注释处理器可以自动生成一个 wiki 页面,描述它期望的资源和类型。
I need to read the value of the value
field in an annotation processor, but I am getting a runtime error.
我需要value
在注释处理器中读取字段的值,但出现运行时错误。
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.
但实际的类对处理器不可用。我想我需要一个javax.lang.model.type.TypeMirror
代替作为真正班级的替代品。我不知道如何得到一个。
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.
这Appointment
是我的一个@Pojo
注释中提到的一个类。
Unfortunately, document and/or tutorials on Java annotation processing seems scarce. Tried googling.
不幸的是,关于 Java 注释处理的文档和/或教程似乎很少。尝试谷歌搜索。
采纳答案by Ralph
Have you read this article: http://blog.retep.org/2009/02/13/getting-class-values-from-annotations-in-an-annotationprocessor/?
您是否阅读过这篇文章: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.
诀窍是实际使用 getAnnotation() 并捕获 MirroredTypeException。令人惊讶的是,异常然后提供了所需类的 TypeMirror。
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.
我不知道这是否是一个好的解决方案,但它是一个。在我个人看来,我会尝试在 MirroredType 后面获取类型,但我不知道这是否可行。
回答by Dave Dopson
I came here to ask the EXACT same question. ... and found the same blog linkposted by Ralph.
我来这里是为了问完全相同的问题。...并找到了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:
当然,一旦你得到一个 TypeMirror,你(至少以我的经验)几乎总是想要一个 TypeElement:
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).
......最后一点不明显的地方在我第一次整理之前花了我一个小时的头发。这些注释处理器实际上一点也不难写,API 起初只是超级混乱,而且冗长得令人费解。我很想推出一个帮助类,使所有基本操作都显而易见……但这是另一天的故事(如果你想要的话,请给我留言)。
回答by tquadrat
My answer is basically the same as that from Dave Dopson, only the code was changed a bit:
我的回答与 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;
}
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.
他的代码用作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 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).
Dave 用于Element.getElementValues()
获取注释值,但这仅返回那些显式应用于具体注释的值。默认值将被省略。该方法Elements.getElementValuesWithDefaults()
还将返回默认值(您可能已经从它的名称中猜到了)。
回答by mnesarco
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:
然后我可以使用任何 Annotation 值:
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
使用 JDK 1.8.0_192 测试