javadoc:ClassDoc中查找指定方法(Method)对应的MethodDoc对象
javadoc的ClassDoc对象完整且结构化保存了一个类代码的所有注释信息,
如果你对JavaDoc工具还不太了解,请参考我之前写的博客《java:通过javadoc API读取java源码中的注释信息(comment)》
本文要说明的是如何从已经得到的com.sun.javadoc.ClassDoc实例中获取指定方法的注释对象(MethodDoc)。
JavaDoc将Java代码注释信息解析是一个相当结构化的一组对象。
Java中对一个类的所有描述对象Class,Method,Field,Constructor,Type,…JavaDoc中都有对应的对象:
| Java 类 | JavaDoc 对应的接口类 |
|---|---|
| java.lang.Class | com.sun.javadoc.ClassDoc |
| java.lang.reflect.Method | com.sun.javadoc.MethodDoc |
| java.lang.reflect.Constructor | com.sun.javadoc.ConstructorDoc |
| java.lang.reflect.Field | com.sun.javadoc.FieldDoc |
| java.lang.reflect.Type | com.sun.javadoc.Type |
| java.lang.reflect.ParameterizedType | com.sun.javadoc.ParameterizedType |
| java.lang.reflect.TypeVariable | com.sun.javadoc.TypeVariable |
| java.lang.reflect.WildcardType | com.sun.javadoc.WildcardType |
但是JavaDoc是一套独立的代码,它本身并没有提供这与Java 类之前的互操作功能。
所以虽然ClassDoc中有methods()方法可以获取所有的MethodDoc对象,但是对于从ClassDoc找一个方法java.lang.reflect.Method对应的MethodDoc对象,ClassDoc中并没有提供这样的功能。
方案一
首先说明这是一个讨巧的方案,但也是不安全的方案。
我查看了ClassDoc接口的实现类com.sun.tools.javadoc.ClassDocImpl,发现它是有提供findMethod,findConstructor,findField方法来获取MethodDoc,ConstructorDoc,FieldDoc对象的。
于是直接调用ClassDocImpl提供的方法就能正确查找到方法对应的注释对象
ClassDocImpl是不开源的,看起来麻烦点,但是能看到方法定义看应该与Class查找方法的用法差不多,
如 ClassDocImpl.findMethod(String var1, String[] var2)
虽然看不到参数名,但对比Class.getMethod(String name, Class... parameterTypes)应该知道,第一个参数是方法名。
后面的数组是参数类型数组,只是在findMethod中数组类型成了String[]。
能猜到,ClassDocImpl内部是对于参数类型比较是通过字符串比较来实现的。那么只要能将Class转为ClassDocImpl可以正确匹配的字符串,应该就能正确找到方法的注释对象
如下是实现代码,其中getTypeName用于将Class转为类的全名字符,这才是实现的关键:
JavaDocUtils.java
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.MemberDoc;
import com.sun.tools.javadoc.ClassDocImpl;
@SuppressWarnings("restriction")
public class JavaDocUtils {
/**
* 返回类型的全名字符串
* @param type
*/
private static final String getTypeName(Class type) {
StringBuilder dimensions = new StringBuilder();
while (type.isArray()) {
dimensions.append("[]");
type = type.getComponentType();
}
String name = type.getName().replaceAll("\\$", "\\.");
return new StringBuilder(name).append(dimensions).toString();
}
/**
* 将类型数组转为对应的全名类型字符串数组
* @param parameterTypes
*/
private static String[] prepareTypes(Class[] parameterTypes) {
String[] typeNames = new String[parameterTypes.length];
for(int i=0;i
typeNames[i] = getTypeName(parameterTypes[i]);
}
return typeNames;
}
/**
* 在{@link ClassDoc}中查找与 {@link Member} 匹配的{@link MemberDoc}
if (null == classDoc || null == member){
return null;
}
ClassDocImpl classDocImpl=(ClassDocImpl)classDoc;
if(member instanceof Method) {
return classDocImpl.findMethod(member.getName(),prepareTypes(((Method)member).getParameterTypes()));
}else if(member instanceof Constructor type) {
StringBuilder dimensions = new StringBuilder();
while (type.isArray()) {
dimensions.append("[]");
type = type.getComponentType();
}
String name = type.getName().replaceAll("\\$", "\\.");
return new StringBuilder(name).append(dimensions).toString();
}
/**
* 如果两个类型字符串匹配,返回{@code true},否则返回{@code false}
* @param docType
* @param type
*/
private static boolean equalType(com.sun.javadoc.Type docType,Class type) {
String typeName = getTypeName(type);
String paramName = docType.qualifiedTypeName()+docType.dimension();
if(typeName.equals(paramName)) {
return true;
}
/**
* primitive为true,说明docType 代表的类型没有对应的源码注释,
* 这时qualifiedTypeName()返回只是该类型的类名(不包含包名) ,
* 所以尝试使用Class的simpleName来比较
*/
return (docType.isPrimitive() && docType.qualifiedTypeName().equals(((Class)type).getSimpleName()));
}
/**
* 检查两个方法对象的签名是否匹配
* @param member
* @param doc
* @return 不匹配返回 {@code false} ,匹配返回 {@code true}
*/
private static boolean match(Member member, MemberDoc doc) {
if (!member.getName().equals(doc.name())){
return false;
}
if(member instanceof Field) {
return true;
}
Class[] paramTypes;
if(member instanceof Method){
paramTypes = ((Method)member).getParameterTypes();
}else if(member instanceof Constructor){
paramTypes = ((Constructor)member).getParameterTypes();
}else{
throw new IllegalArgumentException(String.format("INVALID member type %s,Method or Constructor required",member.getClass().getSimpleName()));
}
if(!(doc instanceof ExecutableMemberDoc)) {
throw new IllegalArgumentException(String.format("INVALID doc type %s,ExecutableMemberDoc required",doc.getClass().getSimpleName()));
}
Parameter[] parameters = ((ExecutableMemberDoc)doc).parameters();
if (paramTypes.length != parameters.length) {
return false;
}
for (int i = 0; i
* 没找到匹配的对象则返回{@code null}
* @param classDoc
* @param member
*/
public static MemberDoc findMember(ClassDoc classDoc,Member member) {
if (null == classDoc || null == member){
return null;
}
if(!equalType(classDoc,member.getDeclaringClass())) {
return null;
}
MemberDoc[] memberDocs;
if(member instanceof Field) {
memberDocs = classDoc.fields();
}else if(member instanceof Method){
memberDocs = classDoc.methods();
}else if(member instanceof Constructor){
memberDocs = classDoc.constructors();
}else{
throw new IllegalArgumentException(String.format("INVALID member type %s,Field,Method or Constructor required",member.getClass().getSimpleName()));
}
for (int i = 0 ; i
* @see #findMember(ClassDoc, Member)
*/
private static MemberDoc getMemberDoc(ClassDoc classDoc,Member member) {
if (null == classDoc || null == member){
return null;
}
MemberDoc matched = findMember(classDoc, member);
if(matched == null){
return getMemberDoc(classDoc.superclass(), member);
}
return matched;
}
}
findMember(ClassDoc classDoc,Member member)方法实现在ClassDoc中查找与Member匹配的MemberDoc对象,没找到匹配的对象则返回nulll.
getMemberDoc(ClassDoc classDoc,Member member)方法调用findMember在当前ClassDoc上如果没找到匹配的对象则尝试在父类的ClassDoc对象上递归查找。
ClassDoc.overriddenMethod
按照我们的代码习惯,子类重写的方法(@Override)上一般没有注释,如果获取方法的注释对象为空,就要尝试向上查找父类方法才能找到真正的方法注释,这里就要用到ClassDoc.overriddenMethod()方法,如果当前方法对象是重写方法(@Override),它可以返回方法父类方法,否则返回null。
于是我们对于获取方法注释的实现可以实现如下:
/**
* 在{@link ClassDoc}中查找与 {@link Method} 匹配的{@link MethodDoc}
* 如果没有在当前方法上找到注释且是重写方法,则尝试向上父类查找父类方法
* @param classDoc
* @param method
* @return 没有找则返回{@code null}
* @see #getMemberDoc(ClassDoc, Member)
*/
public static MethodDoc getMethodDoc(ClassDoc classDoc, Method method) {
MethodDoc doc = (MethodDoc) getMemberDoc(classDoc,method);
while(null != doc && Strings.isNullOrEmpty(doc.getRawCommentText())){
// 如果没有注释,向上父类查找被重写的方法
doc = doc.overriddenMethod();
}
return doc;
}
javadocreader
以上代码完整应用参见码云仓库:
common-javadocreader/src/main/java/gu/doc/ExtClassDoc.java · 10km/common-java - 码云 - 开源中国 (gitee.com)
