javadoc:ClassDoc中查找指定方法(Method)对应的MethodDoc对象

2024-06-15 1076阅读

javadoc的ClassDoc对象完整且结构化保存了一个类代码的所有注释信息,

javadoc:ClassDoc中查找指定方法(Method)对应的MethodDoc对象
(图片来源网络,侵删)

如果你对JavaDoc工具还不太了解,请参考我之前写的博客《java:通过javadoc API读取java源码中的注释信息(comment)》

本文要说明的是如何从已经得到的com.sun.javadoc.ClassDoc实例中获取指定方法的注释对象(MethodDoc)。

JavaDoc将Java代码注释信息解析是一个相当结构化的一组对象。

Java中对一个类的所有描述对象Class,Method,Field,Constructor,Type,…JavaDoc中都有对应的对象:

Java 类JavaDoc 对应的接口类
java.lang.Classcom.sun.javadoc.ClassDoc
java.lang.reflect.Methodcom.sun.javadoc.MethodDoc
java.lang.reflect.Constructorcom.sun.javadoc.ConstructorDoc
java.lang.reflect.Fieldcom.sun.javadoc.FieldDoc
java.lang.reflect.Typecom.sun.javadoc.Type
java.lang.reflect.ParameterizedTypecom.sun.javadoc.ParameterizedType
java.lang.reflect.TypeVariablecom.sun.javadoc.TypeVariable
java.lang.reflect.WildcardTypecom.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)

VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]