在注释处理器中获取字段类

时间:2013-07-15 17:43:31

标签: java annotations

我正在编写我的第一个Annotations处理器,并且遇到一些看似微不足道的问题,但我找不到任何有关它的信息。

我有一个用我的注释注释的元素

@MyAnnotation String property;

当我将此属性作为处理器中的元素时,我似乎无法以任何方式获取元素的类型。在这种情况下,a希望获得表示String的Class或TypeElement实例。

我尝试使用Class.forName()实例化容器类型的类对象,但它抛出了ClassNotFoundException。我想这是因为我无法访问包含类的类加载器?

1 个答案:

答案 0 :(得分:52)

运行注释处理器时,您无权访问已编译的类。注释处理的重点是它发生在预编译之前。

相反,您需要创建一个专门处理注释类型的注释处理器,然后使用镜像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<VariableElement> fields = ElementFilter.fieldsIn(elements);
        for (VariableElement field : fields) {
            TypeMirror fieldType = field.asType();
            String fullTypeClassName = fieldType.toString();
            // Validate fullTypeClassName
        }
        return true;
    }
}

对于验证,不能使用类似MyType.class之类的任何尚未编译的类(包括那些即将使用注释编译的类)。对于这些,您必须仅使用字符串。这是因为注释处理发生在称为“源代码生成”的预编译阶段,这使得您可以在编译器使用注释运行之前生成源代码。

验证字段类型为java.lang.String(已编译)的示例验证:

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

<强>资源

修改

  

我想获取字段类型以获取该类型的注释。但这似乎不可能吗?

确实有可能!这可以使用javax上的更多方法来完成:

TypeMirror

从这里,你有两个选择:

  1. 如果您要查找的注释已经编译(即它来自另一个库),那么您可以直接引用该类来获取注释。
  2. 如果您要查找的注释未编译(即它正在当前正在运行APT的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(); 编译中编译),那么您可以通过javac实例引用它。
  3. 已编译

    AnnotationMirror

    非常简单,这使您可以直接访问注释本身。

    未编译

    请注意,无论注释是否已编译,此解决方案都能正常工作,它不像上面的代码那样干净。

    以下是我编写的一些方法,用于通过类名称从注释镜像中提取某个值:

    DifferentAnnotation diffAnn = fieldTypeElement.getAnnotation(
            DifferentAnnotation.class);
    // Process diffAnn
    

    假设您正在寻找private static <T> T findAnnotationValue(Element element, String annotationClass, String valueName, Class<T> 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> T extractValue(AnnotationMirror annotationMirror, String valueName, Class<T> expectedType) { Map<ExecutableElement, AnnotationValue> elementValues = new HashMap<ExecutableElement, AnnotationValue>( annotationMirror.getElementValues()); for (Entry<ExecutableElement, AnnotationValue> 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