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