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

2024-05-04

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

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

@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<VariableElement> fields = ElementFilter.fieldsIn(elements);
        for (VariableElement field : fields) {
            TypeMirror fieldType = field.asType();
            String fullTypeClassName = fieldType.toString();
            // Validate fullTypeClassName
        }
        return true;
    }
}

为了验证,您cannot使用任何尚未编译的类(包括即将使用注释编译的类),使用类似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);
    }
}

资源

  • APT 主页面 http://docs.oracle.com/javase/7/docs/technotes/guides/apt/
  • 镜像 API Javadocs(Java 7 及更早版本) http://docs.oracle.com/javase/7/docs/jdk/api/apt/mirror/overview-summary.html
  • Edit: Mirror API Javadocs (Java 8) https://docs.oracle.com/javase/8/docs/api/javax/lang/model/package-summary.html
    • 请注意,镜像 API 现在已在 Java 8 中标准化javax.lang.model并且旧的 API 已被弃用。看这篇博文 https://blogs.oracle.com/darcy/entry/an_apt_replacement了解更多信息。如果您一直在使用javax课程,那么你就不用担心了。

Edit:

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

确实有可能!这可以使用更多方法来完成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();

从这里,您有两个选择:

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

String diffAnnotationName = findAnnotationValue(fieldTypeElement,
        "com.example.DifferentAnnotation", "name", String.class);
System.out.println(diffAnnotationName);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在注释处理器中获取字段类 的相关文章

随机推荐