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

问题描述

我正在编写我的第一个 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 页面
  • 镜像 API Javadocs (Java 7及以上)
  • 镜像 API Javadocs (Java 8)
    • 请注意,镜像 API 现在在 javax.lang.model 下的 Java 8 中标准化,旧 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.

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

      确实有可能!这可以使用 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. 如果您尝试查找的注释未编译(即它正在运行 APT 的 javac 的当前调用中编译),那么您可以通过 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;
      
          // ...
      }
      

      此代码将打印My Class:

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

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

08-19 13:32