使用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 extends TypeMirror> 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 extends TypeMirror> 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 extends TypeElement> 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)){ ...
这应该使它工作