在注释处理器中获取字段类 [英] Get field class in annotations processor

查看:120
本文介绍了在注释处理器中获取字段类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am writing my first Annotations processor and having trouble with something that seems trivial but I cannot find any information about it.

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

I have a element annotated with my annotation

@MyAnnotation String property;

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

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.

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

I tried instantiating a class object of the container type with Class.forName() but it threw a 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.

相反,您需要创建一个专门处理注释类型的注释处理器,然后使用镜像API访问该领域。例如:

Instead, you need to create an annotation processor that specifically handles your annotation type, then use the mirror API to access the field. 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;
    }
}

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

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. 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.

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

An example validation verifying that the field type is java.lang.String (which is already compiled):

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及更早版本)

  • 编辑: Mirror API Javadocs(Java 8)


    • 请注意,镜像API现在在Java 8中以 javax.lang.model 标准化,旧的API是弃用。有关详细信息,请参阅此博客文章。如果您一直在使用 javax 类,那么您无需担心。

    • Main APT Page
    • Mirror API Javadocs (Java 7 and older)
    • Mirror API Javadocs (Java 8)
      • Note that the mirror API is now standardized in Java 8 under javax.lang.model and the old API is deprecated. See this blog post for more information. If you've been using the javax classes, then you don't need to worry.

      修改:


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

      I want to get the field type to get annotations on that type. But this does not seem like it will be possible?

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

      Indeed it is possible! This can be done using more methods on the 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. 如果您要查找的注释已经编译(即它来自另一个库),那么您可以直接引用该类来获取注释。

      2. 如果您要查找的注释未编译(即它正在当前调用 javac 编译运行APT),那么您可以通过<$引用它c $ c> AnnotationMirror 个实例。

      1. If the annotation you're trying to find is already compiled (i.e. 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 (i.e. it's being compiled in the current call to javac that's running the APT) then you can reference it via AnnotationMirror instances.

      已编译

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

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

      Very straight-forward, this gives you direct access to the annotation itself.

      未编译

      请注意,此解决方案无论是否有效都可以使用或者没有编译注释,它只是不像上面的代码那样干净。

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

      假设您正在寻找 DifferentAnnotation 注释,您的源代码如下所示:

      Let's say that you're looking for the DifferentAnnotation annotation and your source code looks like this:

      @DifferentAnnotation(name = "My Class")
      public class MyClass {
      
          @MyAnnotation
          private String field;
      
          // ...
      }
      

      此代码将print 我的班级

      This code will print My Class:

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

      这篇关于在注释处理器中获取字段类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆