使用Java 6注释处理器获取generics类型的限定类名

我正在使用JDK 6的Annotation Processing API开发一个小代码生成器,并试图获得类中字段的实际generics类型。 为了更清楚,让我说我有这样一个类:

@MyAnnotation public class User { private String id; private String username; private String password; private Set roles = new HashSet(); private UserProfile profile; } 

这是我的注释处理器类:

 @SupportedAnnotationTypes({ "xxx.MyAnnotation" }) @SupportedSourceVersion(SourceVersion.RELEASE_6) public class MongoDocumentAnnotationProcessor extends AbstractProcessor { private Types typeUtils = null; private Elements elementUtils = null; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); typeUtils = processingEnv.getTypeUtils(); elementUtils = processingEnv.getElementUtils(); } @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { debug("Running " + getClass().getSimpleName()); if (roundEnv.processingOver() || annotations.size() == 0) { return false; } for (Element element : roundEnv.getRootElements()) { if (element.getKind() == ElementKind.CLASS && isAnnotatedWithMongoDocument(element)) { for (VariableElement variableElement : ElementFilter.fieldsIn(element.getEnclosedElements())) { String fieldName = variableElement.getSimpleName().toString(); Element innerElement = typeUtils.asElement(variableElement.asType()); String fieldClass = ""; if (innerElement == null) { // Primitive type PrimitiveType primitiveType = (PrimitiveType) variableElement.asType(); fieldClass = typeUtils.boxedClass(primitiveType).getQualifiedName().toString(); } else { if (innerElement instanceof TypeElement) { TypeElement typeElement = (TypeElement) innerElement; fieldClass = typeElement.getQualifiedName().toString(); TypeElement collectionType = elementUtils.getTypeElement("java.util.Collection"); if (typeUtils.isAssignable(typeElement.asType(), collectionType.asType())) { TypeVariable typeMirror = (TypeVariable)((DeclaredType)typeElement.asType()).getTypeArguments().get(0); TypeParameterElement typeParameterElement = (TypeParameterElement) typeUtils.asElement(typeMirror); // I am stuck here. I don't know how to get the // full qualified class name of the generic type of // property 'roles' when the code processes the User // class as above. What I want to retrieve is the // 'my.package.Role' value } } } } } } return false; } private boolean isAnnotated(Element element) { List annotationMirrors = element.getAnnotationMirrors(); if (annotationMirrors == null || annotationMirrors.size() == 0) return false; for (AnnotationMirror annotationMirror : annotationMirrors) { String qualifiedName = ((TypeElement)annotationMirror.getAnnotationType().asElement()).getQualifiedName().toString(); if ("xxx.MyAnnotation".equals(qualifiedName)) return true; } return false; } } 

任何暗示都会非常感激!

复制粘贴我的原始答案 :

对于那些来自谷歌的人来说,这似乎是一个常见的问题:有希望。

Dagger DI项目根据Apache 2.0许可证授权,并包含一些用于处理注释处理器中类型的实用方法。

特别是,可以在GitHub( Util.java )上完整地查看Util类,并定义一个方法public static String typeToString(TypeMirror type) 。 它使用TypeVisitor和一些递归调用来构建类型的字符串表示。 这是一个参考片段:

 public static void typeToString(final TypeMirror type, final StringBuilder result, final char innerClassSeparator) { type.accept(new SimpleTypeVisitor6() { @Override public Void visitDeclared(DeclaredType declaredType, Void v) { TypeElement typeElement = (TypeElement) declaredType.asElement(); rawTypeToString(result, typeElement, innerClassSeparator); List typeArguments = declaredType.getTypeArguments(); if (!typeArguments.isEmpty()) { result.append("<"); for (int i = 0; i < typeArguments.size(); i++) { if (i != 0) { result.append(", "); } // NOTE: Recursively resolve the types typeToString(typeArguments.get(i), result, innerClassSeparator); } result.append(">"); } return null; } @Override public Void visitPrimitive(PrimitiveType primitiveType, Void v) { ... } @Override public Void visitArray(ArrayType arrayType, Void v) { ... } @Override public Void visitTypeVariable(TypeVariable typeVariable, Void v) { result.append(typeVariable.asElement().getSimpleName()); return null; } @Override public Void visitError(ErrorType errorType, Void v) { ... } @Override protected Void defaultAction(TypeMirror typeMirror, Void v) { ... } }, null); } 

我忙于自己的项目,它生成类扩展。 Dagger方法适用于复杂情况,包括通用内部类。 我有以下结果:

我的测试类带有扩展字段:

 public class AnnotationTest { ... public static class A { @MyAnnotation private Set> _bs; } public static class B { private T _value; } } 

在处理器为_bs字段提供的Element上调用Dagger方法:

 accessor.type = DaggerUtils.typeToString(element.asType()); 

生成的源(当然是自定义的)。 请注意令人敬畏的嵌套generics类型。

 public java.util.Set> AnnotationTest.A.getBsGenerated() { return this._bs; } 

编辑:调整概念以提取第一个generics参数的TypeMirror,否则返回null:

 public static TypeMirror getGenericType(final TypeMirror type) { final TypeMirror[] result = { null }; type.accept(new SimpleTypeVisitor6() { @Override public Void visitDeclared(DeclaredType declaredType, Void v) { List typeArguments = declaredType.getTypeArguments(); if (!typeArguments.isEmpty()) { result[0] = typeArguments.get(0); } return null; } @Override public Void visitPrimitive(PrimitiveType primitiveType, Void v) { return null; } @Override public Void visitArray(ArrayType arrayType, Void v) { return null; } @Override public Void visitTypeVariable(TypeVariable typeVariable, Void v) { return null; } @Override public Void visitError(ErrorType errorType, Void v) { return null; } @Override protected Void defaultAction(TypeMirror typeMirror, Void v) { throw new UnsupportedOperationException(); } }, null); return result[0]; } 

看起来有几个问题。 一,isAssignable()不按预期工作。 其次,在上面的代码中,您试图获取Set类型(T)的generics参数,而不是变量声明(Role)。

不过,以下代码应certificate您需要的内容:

 @SupportedAnnotationTypes({ "xxx.MyAnnotation" }) @SupportedSourceVersion(SourceVersion.RELEASE_6) public class MongoDocumentAnnotationProcessor extends AbstractProcessor { @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); } @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { if (roundEnv.processingOver() || annotations.size() == 0) { return false; } for (Element element : roundEnv.getRootElements()) { if (element.getKind() == ElementKind.CLASS && isAnnotatedWithMongoDocument(element)) { System.out.println("Running " + getClass().getSimpleName()); for (VariableElement variableElement : ElementFilter.fieldsIn(element.getEnclosedElements())) { if(variableElement.asType() instanceof DeclaredType){ DeclaredType declaredType = (DeclaredType) variableElement.asType(); for (TypeMirror typeMirror : declaredType.getTypeArguments()) { System.out.println(typeMirror.toString()); } } } } } return true; //processed } private boolean isAnnotatedWithMongoDocument(Element element) { return element.getAnnotation(MyAnnotation.class) != null; } } 

此代码应输出:

 xxx.Role 

所有其他答案,虽然有很多好点。 不要真的告诉你你有什么问题,这是解决方案。

您的代码中的问题就在这里

 TypeElement collectionType = elementUtils.getTypeElement("java.util.Collection"); if (typeUtils.isAssignable(typeElement.asType(), collectionType.asType())) { ... 

您的类型不是扩展java.util.Collection而是扩展java.util.Collection<*> 。 让我们重写上面的块以反映这一点:

 WildcardType WILDCARD_TYPE_NULL = this.typeUtils.getWildcardType(null, null); final TypeElement collectionTypeElement = this.elementUtils.getTypeElement(Collection.class.getName()); TypeMirror[] typex = {WILDCARD_TYPE_NULL}; DeclaredType collectionType=this.typeUtils.getDeclaredType(collectionTypeElement, typex); if (typeUtils.isAssignable(typeElement.asType(), collectionType)){ ... 

这应该使它工作