在注释处理器中获取字段类
我正在编写我的第一个Annotations处理器,并且遇到一些看似微不足道的问题,但我找不到任何有关它的信息。
我有一个用我的注释注释的元素
@MyAnnotation String property;
当我将此属性作为处理器中的元素时,我似乎无法以任何方式获取元素的类型。 在这种情况下,a希望获得表示String的Class或TypeElement实例。
我尝试使用Class.forName()
实例化容器类型的类对象,但它抛出了ClassNotFoundException。 我想这是因为我无法访问包含该类的类加载器?
运行注释处理器时,您无权访问已编译的类。 注释处理的重点是它发生在预编译之前。
相反,您需要创建专门处理注释类型的注释处理器,然后使用镜像API访问该字段。 例如:
@SupportedAnnotationTypes("com.example.MyAnnotation") public class CompileTimeAnnotationProcessor extends AbstractProcessor { @Override public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) { // Only one annotation, so just use annotations.iterator().next(); Set extends Element> elements = roundEnv.getElementsAnnotatedWith( annotations.iterator().next()); Set fields = ElementFilter.fieldsIn(elements); for (VariableElement field : fields) { TypeMirror fieldType = field.asType(); String fullTypeClassName = fieldType.toString(); // Validate fullTypeClassName } return true; } }
对于validation,您不能使用类似MyType.class
类的任何尚未编译的类(包括那些即将使用注释编译的类)。 对于这些,您必须仅使用字符串 。 这是因为注释处理发生在称为“源代码生成”的预编译阶段,这是允许您在编译器使用注释运行之前生成源代码的原因。
validation字段类型是java.lang.String
(已经编译)的示例validation:
for (VariableElement field : fields) { TypeMirror fieldType = field.asType(); String fullTypeClassName = fieldType.toString(); if (!String.class.getName().equals(fullTypeClassName)) { processingEnv.getMessager().printMessage( Kind.ERROR, "Field type must be java.lang.String", field); } }
资源
- 主要APT页面
- Mirror API Javadocs(Java 7及更早版本)
- 编辑: 镜像API Javadocs(Java 8)
- 请注意,镜像API现在在Java 8中在
javax.lang.model
下javax.lang.model
并且不推荐使用旧API。 有关更多信息,请参阅此博客文章 。 如果您一直在使用javax
类,那么您不必担心。
- 请注意,镜像API现在在Java 8中在
编辑:
我想获取字段类型以获取该类型的注释。 但这似乎不太可能吗?
确实有可能! 这可以使用TypeMirror
上的更多方法来完成:
if (fieldType.getKind() != TypeKind.DECLARED) { processingEnv.getMessager().printMessage( Kind.ERROR, "Field cannot be a generic type.", field); } DeclaredType declaredFieldType = (DeclaredType) fieldType; TypeElement fieldTypeElement = (TypeElement) declaredFieldType.asElement();
从这里,您有两个选择:
- 如果您要查找的注释已经编译(即它来自另一个库),那么您可以直接引用该类来获取注释。
- 如果您正在尝试查找的注释未编译(即,它正在当前调用运行APT的
javac
),那么您可以通过AnnotationMirror
实例引用它。
已编译
DifferentAnnotation diffAnn = fieldTypeElement.getAnnotation( DifferentAnnotation.class); // Process diffAnn
非常简单,这使您可以直接访问注释本身。
没编译
请注意,无论注释是否已编译,此解决方案都将起作用,它不像上面的代码那样干净。
以下是我编写的一些方法,用于通过类名从注释镜像中提取某个值:
private static T findAnnotationValue(Element element, String annotationClass, String valueName, Class expectedType) { T ret = null; for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) { DeclaredType annotationType = annotationMirror.getAnnotationType(); TypeElement annotationElement = (TypeElement) annotationType .asElement(); if (annotationElement.getQualifiedName().contentEquals( annotationClass)) { ret = extractValue(annotationMirror, valueName, expectedType); break; } } return ret; } private static T extractValue(AnnotationMirror annotationMirror, String valueName, Class expectedType) { Map elementValues = new HashMap( annotationMirror.getElementValues()); for (Entry entry : elementValues .entrySet()) { if (entry.getKey().getSimpleName().contentEquals(valueName)) { Object value = entry.getValue().getValue(); return expectedType.cast(value); } } return null; }
假设您正在寻找DifferentAnnotation
注释,您的源代码如下所示:
@DifferentAnnotation(name = "My Class") public class MyClass { @MyAnnotation private String field; // ... }
此代码将打印My Class
:
String diffAnnotationName = findAnnotationValue(fieldTypeElement, "com.example.DifferentAnnotation", "name", String.class); System.out.println(diffAnnotationName);