如何基于新Javadoc API(jdk.javadoc.doclet.*)通过自定义Doclet修改注释?
在新Javadoc API中修改注释的实现方案
新的jdk.javadoc.doclet.* API采用不可变模型设计,没有旧API里setRawCommentText()这类直接修改注释的方法。要实现自定义修改注释和方法参数内容,同时保留默认HTML生成流程,得通过扩展标准Doclet并拦截注释树处理来完成——核心思路是创建修改后的注释树节点替换原内容,再传递给后续生成逻辑。
实现步骤与代码示例
1. 自定义Doclet继承StandardDoclet
继承标准Doclet,重写generate()方法,在默认HTML生成前拦截并修改注释:
import jdk.javadoc.doclet.DocletEnvironment; import jdk.javadoc.doclet.StandardDoclet; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; public class CustomCommentDoclet extends StandardDoclet { @Override public void generate(DocletEnvironment env) { // 遍历所有方法,修改其注释 for (Element element : env.getIncludedElements()) { if (element instanceof ExecutableElement method) { modifyMethodComment(env, method); } } // 调用父类方法执行默认HTML生成 super.generate(env); } private void modifyMethodComment(DocletEnvironment env, ExecutableElement method) { var docTrees = env.getDocTrees(); var originalDocTree = docTrees.getDocCommentTree(method); if (originalDocTree == null) return; // 通过自定义Visitor生成修改后的注释树 var modifiedDocTree = originalDocTree.accept(new CommentRewriteVisitor(), null); // 注意:新API未公开绑定修改后注释树的接口,可通过反射绕过不可限制,或在渲染阶段拦截替换 } }
2. 自定义DocTreeVisitor修改注释节点
通过访问者模式遍历注释树节点,替换目标内容(比如修改主注释、调整@param标签):
import com.sun.source.doctree.*; import com.sun.source.util.SimpleDocTreeVisitor; import java.util.ArrayList; import java.util.List; public class CommentRewriteVisitor extends SimpleDocTreeVisitor<DocCommentTree, Void> { // 要替换的新注释内容 private final String newMainComment = "【已修改】这是方法的新注释"; // 要替换的参数注释内容 private final String newParamComment = "【已修改】这是参数的新注释"; @Override public DocCommentTree visitDocComment(DocCommentTree tree, Void p) { // 构建新的主注释内容 List<DocTree> newFullBody = new ArrayList<>(); newFullBody.add(new TextTree() { @Override public String getBody() { return newMainComment; } @Override public Kind getKind() { return Kind.TEXT; } }); // 构建修改后的块标签(示例:修改第一个@param标签) List<DocTree> newBlockTags = new ArrayList<>(tree.getBlockTags()); for (int i = 0; i < newBlockTags.size(); i++) { DocTag tag = (DocTag) newBlockTags.get(i); if (tag.getKind() == Kind.PARAM) { ParamTree originalParam = (ParamTree) tag; ParamTree newParam = new ParamTree() { @Override public boolean isTypeParameter() { return originalParam.isTypeParameter(); } @Override public Identifier getName() { return originalParam.getName(); } @Override public List<? extends DocTree> getDescription() { List<DocTree> desc = new ArrayList<>(); desc.add(new TextTree() { @Override public String getBody() { return newParamComment; } @Override public Kind getKind() { return Kind.TEXT; } }); return desc; } @Override public Kind getKind() { return Kind.PARAM; } }; newBlockTags.set(i, newParam); break; // 仅修改第一个param标签,按需调整 } } // 返回新的注释树实例 return new DocCommentTree() { @Override public List<? extends DocTree> getFirstSentence() { return tree.getFirstSentence(); } @Override public List<? extends DocTree> getFullBody() { return newFullBody; } @Override public List<? extends DocTree> getBlockTags() { return newBlockTags; } @Override public DocTag getDeprecatedTag() { return tree.getDeprecatedTag(); } @Override public List<? extends DocTag> getSeeTags() { return tree.getSeeTags(); } }; } }
兼容替代方案:拦截HTML输出流程
如果不想依赖内部API或反射,可通过自定义Writer在HTML生成前替换注释文本:
import jdk.javadoc.doclet.StandardDoclet; import jdk.javadoc.internal.doclets.formats.html.HtmlDoclet; import jdk.javadoc.internal.doclets.formats.html.writers.MethodWriter; public class CustomOutputDoclet extends StandardDoclet { @Override protected HtmlDoclet createHtmlDoclet() { return new CustomHtmlDoclet(this); } private static class CustomHtmlDoclet extends HtmlDoclet { public CustomHtmlDoclet(StandardDoclet doclet) { super(doclet); } @Override protected MethodWriter createMethodWriter() { return new CustomMethodWriter(this); } } private static class CustomMethodWriter extends MethodWriter { public CustomMethodWriter(HtmlDoclet doclet) { super(doclet); } @Override protected void writeComment() { // 获取原注释并修改 String originalComment = getElement().getDocComment(); String modifiedComment = originalComment.replace("旧注释内容", "新注释内容"); // 输出修改后的注释 print(modifiedComment); } } }
关键注意事项
- 新API的DocTree节点为不可变设计,必须创建新节点替换原有节点,不能直接修改。
- 依赖
jdk.javadoc.internal包的方案属于内部API调用,不同JDK版本可能存在兼容性问题,需谨慎使用。 - 若需修改方法参数注释,重点处理
ParamTree类型节点,对应Javadoc中的@param标签。
内容的提问来源于stack exchange,提问作者RTAGLIA
相关产品推荐
相关产品推荐

