You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于新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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 03:44:53