java 在注释处理器中获取字段类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17660469/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Get field class in annotations processor
提问by Emil Sj?lander
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. I think this is because I do not have access to the class loader containing the class?
我尝试实例化容器类型的类对象,Class.forName()
但它抛出了 ClassNotFoundException。我认为这是因为我无权访问包含该类的类加载器?
回答by Brian
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. For example:
相反,您需要创建一个专门处理您的注释类型的注释处理器,然后使用镜像 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;
}
}
For the validation, you cannotuse 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.
对于验证,您不能使用诸如MyType.class
. 对于这些,您必须仅使用字符串。这是因为注释处理发生在称为“源代码生成”的预编译阶段,这允许您在编译器使用注释运行之前生成源代码。
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
资源
- Main APT Page
- Mirror API Javadocs (Java 7 and older)
- Edit: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 postfor more information. If you've been using thejavax
classes, then you don't need to worry.
- Note that the mirror API is now standardized in Java 8 under
- APT主页
- 镜像 API Javadocs(Java 7 及更早版本)
- 编辑:镜像 API Javadocs (Java 8)
- 请注意,镜像 API 现在已在 Java 8 下标准化,
javax.lang.model
旧 API 已弃用。有关更多信息,请参阅此博客文章。如果您一直在使用这些javax
类,那么您无需担心。
- 请注意,镜像 API 现在已在 Java 8 下标准化,
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:
从这里,您有两个选择:
- 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.
- 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 viaAnnotationMirror
instances.
- 如果您尝试查找的注释已经编译(即它来自另一个库),那么您可以直接引用该类以获取注释。
- 如果您尝试查找的注释未编译(即它正在
javac
运行 APT的当前调用中编译),那么您可以通过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);