简体   繁体   English

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

[英]Get field class in annotations processor

I am writing my first Annotations processor and having trouble with something that seems trivial but I cannot find any information about it. 我正在编写我的第一个Annotations处理器,并且遇到一些看似微不足道的问题,但我找不到任何有关它的信息。

I have a element annotated with my annotation 我有一个用我的注释注释的元素

@MyAnnotation String property;

When I get this property as a element in my processor I can not seem to get the type of the element in any way. 当我将此属性作为处理器中的元素时,我似乎无法以任何方式获取元素的类型。 In this case a would want to get a Class or TypeElement instance representing String. 在这种情况下,a希望获得表示String的Class或TypeElement实例。

I tried instantiating a class object of the container type with Class.forName() but it threw a ClassNotFoundException. 我尝试使用Class.forName()实例化容器类型的类对象,但它抛出了ClassNotFoundException。 I think this is because I do not have access to the class loader containing the class? 我想这是因为我无法访问包含该类的类加载器?

When running your annotation processor, you don't have access to the compiled classes. 运行注释处理器时,您无权访问已编译的类。 The point of annotation processing is that it happens pre-compile. 注释处理的重点是它发生在预编译之前。

Instead, you need to create an annotation processor that specifically handles your annotation type, then use the mirror API to access the field. 相反,您需要创建专门处理注释类型的注释处理器,然后使用镜像API访问该字段。 For example: 例如:

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

For the validation, you cannot use any classes which have yet to be compiled (including those that are about to be compiled with the annotation) using something like MyType.class . 对于验证,您不能使用类似MyType.class类的任何尚未编译的类(包括那些即将使用注释编译的类)。 For these, you must use strings only . 对于这些,您必须使用字符串 That is because annotation processing occurs during a pre-compiling phase known as "source generation", which is what allows you to generate source code before the compiler runs using annotations. 这是因为注释处理在称为“源代码”的预编译阶段发生,这使得您可以在编译器使用注释运行之前生成源代码。

An example validation verifying that the field type is java.lang.String (which is already compiled): 验证字段类型是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);
    }
}

Resources 资源

Edit: 编辑:

I want to get the field type to get annotations on that type. 我想获取字段类型以获取该类型的注释。 But this does not seem like it will be possible? 但这似乎不可能吗?

Indeed it is possible! 确实有可能! This can be done using more methods on the TypeMirror : 这可以使用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();

From here, you have two choices: 从这里,您有两个选择:

  1. If the annotation you're trying to find is already compiled (ie it's from another library) then you can reference the class directly to get the annotation. 如果您要查找的注释已经编译(即它来自另一个库),那么您可以直接引用该类来获取注释。
  2. If the annotation you're trying to find is not compiled (ie it's being compiled in the current call to javac that's running the APT) then you can reference it via AnnotationMirror instances. 如果您正在尝试查找的注释未编译(即,它是在当前对运行APT的javac的调用中javac的),那么您可以通过AnnotationMirror实例引用它。

Already Compiled 已编译

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

Very straight-forward, this gives you direct access to the annotation itself. 非常简单,这使您可以直接访问注释本身。

Not Compiled 没编译

Note that this solution will work regardless of whether or not the annotation is compiled, it's just not as clean as the code above. 请注意,无论注释是否已编译,此解决方案都将起作用,它不像上面的代码那样干净。

Here are a couple methods I wrote once to extract a certain value from an annotation mirror by its class name: 以下是我编写的一些方法,用于通过类名从注释镜像中提取某个值:

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

Let's say that you're looking for the DifferentAnnotation annotation and your source code looks like this: 假设您正在寻找DifferentAnnotation注释,您的源代码如下所示:

@DifferentAnnotation(name = "My Class")
public class MyClass {

    @MyAnnotation
    private String field;

    // ...
}

This code will print My Class : 此代码将打印My Class

String diffAnnotationName = findAnnotationValue(fieldTypeElement,
        "com.example.DifferentAnnotation", "name", String.class);
System.out.println(diffAnnotationName);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM