javadoc導出成word文檔


 更新說明:

    2021-10-14     增加文字說明,增加注意事項說明

 

 

 

剛剛上次弄完了一個坑爹的任務,這次我領導又給我一個讓人腦瓜子疼的任務了。

 

基本上客戶他在驗收我們系統的時候,都會要求我們編寫相關的文檔,這次也不例外。

只是這次的客戶要求我們給出接口文檔。不僅是要整個controller的rest接口文檔,還要給出service層的接口,連工具類都要有。這不是在為難我胖虎么。

想我辛辛苦苦給全部controller方法都加上了swagger注解,想我辛辛苦苦把整個項目全部方法都加上了完美的javadoc注釋,想我關鍵代碼都留了邏輯注釋,

為什么還要我給你弄個word文檔。

 

抱怨歸抱怨,該干的活還是要干的,誰讓這該死的錢真香。

 

但是要怎么弄呢?這是個很嚴重的問題,畢竟我的項目方法沒一千都有一百,總不能一個個給他Ctrl C + Ctrl V吧,那我的弄到何年何月。

swagger的我可以找工具導出成word文檔,可是service層的呢?我的工具類呢?

 

有沒有現成的工具呢?問下度娘,~~~~~~~~~成功摸魚半天,毫無收獲

那能不能像上次那樣用正則自己搞呢?試一哈 ~~~~~~~~~~~~~ 成功摸魚半天,失敗了,有些代碼的文檔注釋太坑爹了,方法內用文檔注釋,搞事情吧,怎么還有部分前面的*號都沒了。

idea不是可以導出javadoc文檔么?能不能導出word呢?試一哈 ~~~~~~~~~~~~成功摸魚半天,毫無辦法,不過我找到了一個神奇的方法 com.sun.tools.javadoc.Main.execute(initAgrs); 有沒有覺得很眼熟,沒錯,這就是jdk自己的javadoc工具。

 

先讓我們來膜拜下前輩的文章:

https://blog.csdn.net/10km/article/details/78252586

 

原理其實很簡單,javadoc 這個jdk的 tools.jar 中的包的類已經幫我們解析好了java文件,我們需要做的僅僅是重寫一個Doclet,然后在它里面處理下javadoc給我弄好的RootDoc就行,剩下就只剩操作RootDoc它了。

先看下javadoc這個包先

 

 

 其中我們將直接用到com.sun.tools.javadoc.Main這個類

 

 

 

那我們需要做啥呢?最簡單的就3步。

1、創建一個新項目。

2、寫一個自己的Doclet類。

 

3、寫個main方法,里面調用com.sun.tools.javadoc.Main.execute()這個方法,傳入參數

 4、拿到數據后(也就是RootDoc對象),自己解析class類型、方法名、類名、方法注釋、類注釋、字段注釋、參數注釋............然后愛生成HTML、愛生成Word、愛生成Excel,隨你們喜歡(可以在MyDoclet.start()方法里面編寫導出功能)。

 

 

大概怎么用說完了,展示下我的生成后的效果吧,我沒調樣式,畢竟用poi操作word真的是難受

 

 

 

上代碼:

項目結構:(一定要有tools.jar,他在你們jdk的lib目錄里,另外POI操作word的相關包要導齊全,不然各種坑)

 

 

 

 

pom.xml主要就是為了引入POI的包

 

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.hongcheng</groupId>
    <artifactId>javadoc_generator</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>javadoc_generator</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
    
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-scratchpad</artifactId>
            <version>4.0.0</version>
        </dependency>
       
    </dependencies>
</project>

 

 

 

ClassComment.java用來保存類數據的實體
package com.hongcheng.javadoc_generator.entity;

import java.util.List;



/**
 * java類的相關信息
 */
public class ClassComment {
    /** 類的全類名 */
    private String className;
    /** 類的簡單類名 */
    private String simpleClassName;
    /** 類注釋 */
    private String classComment;
    /** 字段相關信息 */
    private List<FieldComment> fields;
    /** 方法相關信息 */
    private List<MethodComment> methods;
    
    
    
    public String getClassName() {
        return className;
    }
    public String getSimpleClassName() {
        return simpleClassName;
    }
    public String getClassComment() {
        return classComment;
    }
    public List<FieldComment> getFields() {
        return fields;
    }
    public List<MethodComment> getMethods() {
        return methods;
    }
    public void setClassName(String className) {
        this.className = className;
    }
    public void setSimpleClassName(String simpleClassName) {
        this.simpleClassName = simpleClassName;
    }
    public void setClassComment(String classComment) {
        this.classComment = classComment;
    }
    public void setFields(List<FieldComment> fields) {
        this.fields = fields;
    }
    public void setMethods(List<MethodComment> methods) {
        this.methods = methods;
    }
    @Override
    public String toString() {
        return "{className: " + className + ", simpleClassName: " + simpleClassName + ", classComment: " + classComment
                + ", fields: " + fields + ", methods: " + methods + "}";
    }
    
    
    
}

 

 

FieldComment.java用來保存字段和參數的實體
package com.hongcheng.javadoc_generator.entity;

/**
 * java類中字段的相關信息
 */
public class FieldComment {
    /** 字段類型 */
    private String clasz;
    /** 類的簡單類名 */
    private String simpleClassName;
    /** 字段注釋 */
    private String fieldComment;
    /** 字段名 */
    private String fieldName;
    /** 默認值,必須是final修飾的基本數據類型及其包裝類 */
    private Object defaultValue;
    
    
    public String getSimpleClassName() {
        return simpleClassName;
    }
    public void setSimpleClassName(String simpleClassName) {
        this.simpleClassName = simpleClassName;
    }
    public String getClasz() {
        return clasz;
    }
    public String getFieldComment() {
        return fieldComment;
    }
    public String getFieldName() {
        return fieldName;
    }
    public Object getDefaultValue() {
        return defaultValue;
    }
    public void setClasz(String clasz) {
        this.clasz = clasz;
    }
    public void setFieldComment(String fieldComment) {
        this.fieldComment = fieldComment;
    }
    public void setFieldName(String fieldName) {
        this.fieldName = fieldName;
    }
    public void setDefaultValue(Object defaultValue) {
        this.defaultValue = defaultValue;
    }
    @Override
    public String toString() {
        return "{clasz: " + clasz + ", simpleClassName: " + simpleClassName + ", fieldComment: " + fieldComment
                + ", fieldName: " + fieldName + ", defaultValue: " + defaultValue + "}";
    }
    
    
}

 

MethodComment.java用來保存方法數據的實體
package com.hongcheng.javadoc_generator.entity;

import java.util.List;


/**
 * java類中方法的相關信息
 */
public class MethodComment {
    /** 方法注釋 */
    private String methodComment;
    /**  方法名 */
    private String methodName;
    /**  參數 */
    private List<FieldComment> params;
    /**  返回值 */
    private FieldComment returnEntity;
    
    
    public String getMethodComment() {
        return methodComment;
    }
    public String getMethodName() {
        return methodName;
    }
    public List<FieldComment> getParams() {
        return params;
    }
    public FieldComment getReturnEntity() {
        return returnEntity;
    }
    public void setMethodComment(String methodComment) {
        this.methodComment = methodComment;
    }
    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }
    public void setParams(List<FieldComment> params) {
        this.params = params;
    }
    public void setReturnEntity(FieldComment returnEntity) {
        this.returnEntity = returnEntity;
    }
    @Override
    public String toString() {
        return "{methodComment: " + methodComment + ", methodName: " + methodName + ", params: " + params
                + ", returnEntity: " + returnEntity + "}";
    }
    
    
}

 

MyDoclet.java很關鍵的類
package com.hongcheng.javadoc_generator;

import com.sun.javadoc.Doclet;
import com.sun.javadoc.RootDoc;

/**
 *     用來獲取javadoc解析完成后生成的語法樹根節點
 * */
public class MyDoclet extends Doclet {
    /**
     *     靜態對象,用於接收javadoc解析完成后生成的語法樹根節點<br>
     *     在后面我們會用他來獲取我們需要的數據
     * */
    private static RootDoc root;
    
    /**
     *     在javadoc解析完java文件后,生成語法樹,然后就會調用這個方法去讓Doclet生成doc文檔
     * */
    public static boolean start(RootDoc rootDoc) {
        MyDoclet.root = rootDoc;
        return true;
    }

    /**
     *     獲取語法樹的根節點
     * */
    public static RootDoc getRoot() {
        return root;
    }
    
}

 

Modifier.java用來處理下哪些方法和字段才是我們需要的
package com.hongcheng.javadoc_generator;

/**
 *     可見性修飾符
 * */
public enum Modifier{
    PUBLIC,PROTECTED,PRIVATE;
}

 

RootClassParser.java用來解析RootDoc,轉成我們自己的那三個實體類
package com.hongcheng.javadoc_generator;

import java.util.LinkedList;
import java.util.List;

import com.hongcheng.javadoc_generator.entity.ClassComment;
import com.hongcheng.javadoc_generator.entity.FieldComment;
import com.hongcheng.javadoc_generator.entity.MethodComment;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.FieldDoc;
import com.sun.javadoc.MethodDoc;
import com.sun.javadoc.ParamTag;
import com.sun.javadoc.Parameter;
import com.sun.javadoc.RootDoc;
import com.sun.javadoc.Tag;
/**
 *     RootClass對象的解析器,用於根據RootClass構建我們自己的ClassComment
 * */
public class RootClassParser {
    
    /** 需要處理的類字段可見性,默認公有 */
    private Modifier fieldModifier = Modifier.PUBLIC;
    /** 需要處理的類方法可見性,默認公有 */
    private Modifier methodModifier = Modifier.PUBLIC;
    
    public RootClassParser(Modifier fieldModifier,Modifier methodModifier) {
        this.fieldModifier = fieldModifier;
        this.methodModifier = methodModifier;
    }
    
    /**
     *     解析
     * */
    public List<ClassComment> parse(RootDoc root) {
        if(root == null) {
            return new LinkedList<ClassComment>();
        }
        List<ClassComment> classComments = new LinkedList<ClassComment>();
        ClassDoc[] classes = root.classes();
        for (ClassDoc clasz:classes) {
            ClassComment classComment = new ClassComment();
            classComment.setClassName(clasz.qualifiedTypeName());
            classComment.setSimpleClassName(clasz.simpleTypeName());
            classComment.setClassComment(clasz.commentText());
            classComment.setFields( this.parseFields(clasz.fields()));
            classComment.setMethods( this.parseMethods(clasz.methods()));
            classComments.add(classComment);
        }
        return classComments;
    }
    
    /**
     *     解析字段
     * */
    private List<FieldComment> parseFields(FieldDoc[] fields){
        if(fields == null || fields.length <= 0) {
            return new LinkedList<FieldComment>();
        }
        List<FieldComment> fieldList = new LinkedList<FieldComment>();
        for (FieldDoc field : fields) {
            if(!this.checkModifier(field)) {
                continue;
            }
            FieldComment fieldComment = new FieldComment();
            fieldList.add(fieldComment);
            fieldComment.setClasz(field.type().qualifiedTypeName());
            fieldComment.setSimpleClassName(field.type().simpleTypeName());
            fieldComment.setFieldComment(field.commentText());
            fieldComment.setFieldName(field.name());
            fieldComment.setDefaultValue(field.constantValue());
        }
        return fieldList;
    }
    
    /**
     *     檢查字段修飾語,也就是public、protected、private
     *     @return 如果該字段的訪問權限修飾語滿足我們需要的級別,那就返回true
     * */
    private boolean checkModifier(FieldDoc field) {
        if(this.getFieldModifier().toString().equalsIgnoreCase(field.modifiers())) {
            return true;
        }
        return false;
    }
    
    /**
     *     檢查方法修飾語,也就是public、protected、private
     *     @return 如果該方法的訪問權限修飾語滿足我們需要的級別,那就返回true
     * */
    private boolean checkModifier(MethodDoc method) {
        if(this.getMethodModifier().toString().equalsIgnoreCase(method.modifiers())) {
            return true;
        }
        return false;
    }
    
    
    
    /**
     *     解析方法
     *     */
    private List<MethodComment> parseMethods(MethodDoc[] methods){
        if(methods == null || methods.length <= 0) {
            return new LinkedList<MethodComment>();
        }
        List<MethodComment> methodsList = new LinkedList<MethodComment>();
        for (MethodDoc method : methods) {
            if(!this.checkModifier(method)) {
                continue;
            }
            MethodComment methodComment = new MethodComment();
            methodsList.add(methodComment);
            methodComment.setMethodComment(method.commentText());
            methodComment.setMethodName(method.name());
            methodComment.setReturnEntity(this.parseMethodReturn(method));
            methodComment.setParams(this.parseMethodParam(method));
        }
        return methodsList;
    }
    
    /***
     *     解析方法的返回值
     * */
    private FieldComment parseMethodReturn(MethodDoc method){
        // 返回值
        FieldComment returnEntity = new FieldComment();
        returnEntity.setClasz(method.returnType().qualifiedTypeName());
        returnEntity.setSimpleClassName(method.returnType().simpleTypeName());
        for(Tag tag:method.tags()) {
            if(tag.name().equals("@return")) {
                returnEntity.setFieldComment(tag.text());
                break;
            }
        }    
        return returnEntity;
    }
    
    
    /***
     *     解析方法的參數
     * */
    private List<FieldComment> parseMethodParam(MethodDoc method){
        // 參數    
        List<FieldComment> params = new LinkedList<FieldComment>();
        for(Parameter parameter:method.parameters()) {
            FieldComment param = new FieldComment();
            param.setClasz(parameter.type().qualifiedTypeName());
            param.setSimpleClassName(parameter.type().simpleTypeName());
            param.setFieldName(parameter.name());
            for(ParamTag paramTag :method.paramTags()) {
                if(paramTag.parameterName().equals(param.getFieldName())) {
                    param.setFieldComment(paramTag.parameterComment());;
                    break;
                }
            }
            params.add(param);
        }
        return params;
    }

    public Modifier getFieldModifier() {
        return fieldModifier;
    }

    public Modifier getMethodModifier() {
        return methodModifier;
    }

    public void setFieldModifier(Modifier fieldModifier) {
        this.fieldModifier = fieldModifier;
    }

    public void setMethodModifier(Modifier methodModifier) {
        this.methodModifier = methodModifier;
    }
    
    
}

 

WordExport.java用來生成word文檔的
package com.hongcheng.javadoc_generator;

import java.io.FileOutputStream;
import java.util.List;

import org.apache.poi.xwpf.usermodel.TableWidthType;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;

import com.hongcheng.javadoc_generator.entity.ClassComment;
import com.hongcheng.javadoc_generator.entity.FieldComment;
import com.hongcheng.javadoc_generator.entity.MethodComment;

public class WordExport {

    public void export(List<ClassComment> result,String path) throws Exception {
        XWPFDocument xwpfDocument = this.newWord();
        
        for(ClassComment classComment: result) {
            this.newParagraph(xwpfDocument, "類名:" + classComment.getClassName());
            this.newParagraph(xwpfDocument, "說明:" + classComment.getClassComment());
            // 字段
            if(classComment.getFields() != null && !classComment.getFields().isEmpty()) {
                XWPFTable table = this.newTable(xwpfDocument, classComment.getFields().size() + 3, 5);
                this.mergeCell(table.getRow(0), 0, 4, "類名:" + classComment.getClassName());
                this.mergeCell(table.getRow(1), 0, 4, "屬性:");
                this.setTableRowText(table.getRow(2), 0, 4, "序號","參數類型","參數名","常量值","說明");
                this.setCellWidth(table.getRow(2), 0, 4, "10%","22.5%","22.5%","22.5%","22.5%");
                
                for(int i = 0,j = 3;i < classComment.getFields().size();i++,j++ ) {
                    FieldComment field = classComment.getFields().get(i);
                    this.setTableRowText(table.getRow(j), 0, 4, String.valueOf(i + 1)
                            ,field.getSimpleClassName()
                            ,field.getFieldName()
                            ,field.getDefaultValue() == null?"":field.getDefaultValue().toString()
                            ,field.getFieldComment());
                }
                this.newBlankLine(xwpfDocument);
                this.newBlankLine(xwpfDocument);
            }
            // 方法
            if(classComment.getMethods() != null && !classComment.getMethods().isEmpty()) {
                for(MethodComment method: classComment.getMethods()) {
                    XWPFTable table = this.newTable(xwpfDocument, 3, 4);
                    this.mergeCell(table.getRow(0), 0, 3, "類名:" + classComment.getClassName());
                    this.mergeCell(table.getRow(1), 0, 3, "方法名:" + method.getMethodName());
                    this.mergeCell(table.getRow(2), 0, 3, "方法說明:" + method.getMethodComment());
                    // 參數
                    if(method.getParams() == null || method.getParams().isEmpty()) {
                        this.mergeCell(table.createRow(), 0, 3, "參數:無" );
                    }else {
                        this.mergeCell(table.createRow(), 0, 3, "參數:" );
                        this.setTableRowText(table.createRow(), 0, 3, "序號","參數類型","參數名","說明");
                        this.setCellWidth(table.getRow(table.getRows().size()-1), 0, 3, "10%","25%","25%","40%");
                        for(int i = 0;i < method.getParams().size(); i++) {
                            FieldComment field = method.getParams().get(i);
                            this.setTableRowText(table.createRow(), 0, 3, String.valueOf(i + 1)
                                    ,field.getSimpleClassName()
                                    ,field.getFieldName()
                                    ,field.getFieldComment());
                        }
                    }
                    // 返回值
                    this.mergeCell(table.createRow(), 0, 3, "返回值:" + method.getReturnEntity().getSimpleClassName() + "  " + method.getMethodComment());
                    this.newBlankLine(xwpfDocument);
                    this.newBlankLine(xwpfDocument);
                }
            }
            this.newBlankLine(xwpfDocument);
            this.newBlankLine(xwpfDocument);
            this.newBlankLine(xwpfDocument);
            this.newBlankLine(xwpfDocument);

        }
        this.writeFile(xwpfDocument, path);
    }
    
    
    
    
    /**
     *     設置單元格寬度
     *     @param row  
     *     @param startCell 起始單元格下標,row的單元格下標從0開始
     *     @param endCell 結束單元格下標
     *     @param percentages 各個單元格的百分百大小,例如"25.5%"
     * */
    private void setCellWidth(XWPFTableRow row,int startCell,int endCell,String ...percentages) {
        if(percentages == null || percentages.length <= 0) {
            throw new IllegalArgumentException("percentages不能為空");
        }
        if((endCell - startCell + 1) > percentages.length) {
            throw new IllegalArgumentException("percentages的元素不夠");
        }
        int i = 0;
        for(XWPFTableCell cell: row.getTableCells()) {
            cell.setWidth(String.valueOf(percentages[i++]));
            cell.setWidthType(TableWidthType.PCT);
        }
    }
    
    
    /**
     *     設置單元格寬度
     *     @param row  
     *     @param startCell 起始單元格下標,row的單元格下標從0開始
     *     @param endCell 結束單元格下標
     *     @param sizes 各個單元格的寬度大小
     * */
    @SuppressWarnings("unused")
    private void setCellWidth(XWPFTableRow row,int startCell,int endCell,int ...sizes) {
        if(sizes == null || sizes.length <= 0) {
            throw new IllegalArgumentException("sizes不能為空");
        }
        if((endCell - startCell + 1) > sizes.length) {
            throw new IllegalArgumentException("sizes的元素不夠");
        }
        int i = 0;
        for(XWPFTableCell cell: row.getTableCells()) {
            cell.setWidth(String.valueOf(sizes[i++]));
            cell.setWidthType(TableWidthType.DXA);
        }
    }
    
    
    
    /**
     *     跨行合並單元格
     *     @param table  
     *     @param startRow 起始行下標,table的行下標從0開始
     *     @param endRow 結束行下標
     *     @param startCell 行內起始單元格下標,row的單元格下標從0開始
     *     @param endCell 行內結束單元格下標
     *     @param text 合並后的單元格文本
     * */
    @SuppressWarnings("unused")
    private void mergeRow(XWPFTable table,int startRow,int endRow,int startCell,int endCell,String text) {
        List<XWPFTableRow> rows = table.getRows();
        for (int j = startRow; j <= endRow; j++) {
            List<XWPFTableCell> tableCells = rows.get(j).getTableCells();
            // 對每個單元格進行操作
            for (int i = startCell; i <= endCell; i++) {
            //對單元格進行合並的時候,要標志單元格是否為起點,或者是否為繼續合並
                if (i == startCell )
                    tableCells.get(i).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
                else
                    tableCells.get(i).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);//繼續合並
            }
        }
        for (int j = startRow; j <= endRow; j++) {
            List<XWPFTableCell> tableCells = rows.get(j).getTableCells();
            // 對每個單元格進行操作
            //對單元格進行合並的時候,要標志單元格是否為起點,或者是否為繼續合並
            if (j == startRow )
                tableCells.get(startCell).getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
            else
                tableCells.get(startCell).getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);//繼續合並
        }
        rows.get(startRow).getCell(startCell).setText(text);//為第1行1到4合並之后的單元格設置內容
    }
    
    
    
    /**
     *     合並表格單元格,針對行內的單元格進行合並
     *     @param row  
     *     @param startCell 起始單元格下標,row的單元格下標從0開始
     *     @param endCell 結束單元格下標
     *     @param text 合並后的單元格文本
     * */
    private void mergeCell(XWPFTableRow row,int startCell,int endCell,String text) {
        List<XWPFTableCell> tableCells = row.getTableCells();
        //     對每個單元格進行操作
        for (int i = startCell; i <= endCell; i++) {
        //對單元格進行合並的時候,要標志單元格是否為起點,或者是否為繼續合並
            if (i == startCell)
                tableCells.get(i).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
            else
                tableCells.get(i).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);//繼續合並
        }
        tableCells.get(startCell).setText(text);//為第1行1到4合並之后的單元格設置內容
    }
    
    
    
    /**
     *     給表格一行賦值,實際設置值是包括首尾單元格的,例如startCell=0,endCell=2,實際會設置0、1、2這三個單元格
     *     @param row
     *     @param startCell 起始單元格下標,row的單元格下標從0開始
     *     @param endCell 結束單元格下標
     *     @param texts 單元格的內容,依次賦值
     * */
    private void setTableRowText(XWPFTableRow row,int startCell,int endCell,String ...texts) {
        if(texts == null || texts.length <= 0) {
            throw new IllegalArgumentException("texts不能為空");
        }
        if((endCell - startCell + 1) > texts.length) {
            throw new IllegalArgumentException("texts的元素不夠");
        }
        List<XWPFTableCell> tableCells = row.getTableCells();
        //     對每個單元格進行操作
        for (int i = startCell,j = 0; i <= endCell; i++,j++) {
            tableCells.get(i).setText(texts[j]);
        }
    }
    
    
    /**
     *     創建一個table
     *     @param xwpfDocument 
     *     @param rowNum 行數
     *     @param colNum 列數
     * */
    private XWPFTable newTable(XWPFDocument xwpfDocument,int rowNum,int colNum) {
        XWPFTable createTable = xwpfDocument.createTable(rowNum, colNum);
        createTable.setWidth("100%");
        createTable.setWidthType(TableWidthType.PCT);
        return createTable;
    }
    
    
    /**
     *     創建一個文本行
     * */
    private XWPFParagraph newParagraph(XWPFDocument xwpfDocument,String text) {
        XWPFParagraph createParagraph = xwpfDocument.createParagraph();
        createParagraph.createRun().setText(text);
        return createParagraph;
    }
    
    /**
     *     創建一個空行
     * */
    private XWPFParagraph newBlankLine(XWPFDocument xwpfDocument) {
        return this.newParagraph(xwpfDocument, "");
    }
    
    /**
     *     創建一個word文檔
     * */
    private XWPFDocument newWord() {
        XWPFDocument xwpfDocument = new XWPFDocument();
        return xwpfDocument;
    }
    
    /**
     *     寫文件
     * */
    private void writeFile(XWPFDocument xwpfDocument,String path) throws Exception {
        xwpfDocument.write(new FileOutputStream("C:\\Users\\HongCheng\\Desktop\\1.docx"));
        xwpfDocument.close();
    }
}

 

JavaDocReader.java用來驗證參數以及調用javadoc.Main類的
package com.hongcheng.javadoc_generator;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.hongcheng.javadoc_generator.entity.ClassComment;
import com.sun.javadoc.RootDoc;
/**
 *     java文件doc讀取器
 * */
public class JavaDocReader {

    /** 用於接收javadoc解析完成后生成的語法樹根節點 */
    private MyDoclet doclet = new MyDoclet();
    /** 類路徑,系統會在類路徑下去找一些相關的類文件 */
    private List<String> classpath ;
    /** java源文件地址。可以是java源文件的絕對地址,也可以是包名。只能填寫一個, */
    private String sourcepath;
    /** 是否遞歸處理子包,只有在sourcepath為包名時才起作用,subpackages不為空是才是true。默認false */
    private boolean isSubpackages = false;
    /** 要遞歸的子包 */
    private String subpackages ;
    /** 需要處理的類字段可見性,默認公有 */
    private Modifier fieldModifier = Modifier.PUBLIC;
    /** 需要處理的類方法可見性,默認公有 */
    private Modifier methodModifier = Modifier.PUBLIC;
    /** jdk的tools.jar的地址 */
    private String jdkToolsJarPath = JavaDocReader.class.getResource("/").getPath() + "/tools.jar";
    
    
    /**
     *     構造函數
     *     @param javaPackage 目標jar包,非空必填
     *    @param subpackages 需要遞歸處理的子包,可以為空
     *    @param classPath 相關的jar包的地址,絕對地址,javaPackage必須要能在這些路徑中找到,非空必填
     * */
    public JavaDocReader(String javaPackage,String subpackages,List<String> classPath) {
        this.init(javaPackage, subpackages, classPath, Modifier.PUBLIC, Modifier.PUBLIC);
    }
    
    

    
    /**
     *     構造函數
     *     @param javaPackage 目標jar包,非空必填
     *    @param subpackages 需要遞歸處理的子包,可以為空
     *    @param classPath 相關的jar包的地址,絕對地址,javaPackage必須要能在這些路徑中找到,非空必填
     *    @param fieldModifier 需要處理的類字段可見性,非空
     *    @param methodModifier 需要處理的類方法可見性,非空
     * */
    public JavaDocReader(String javaPackage,String subpackages,List<String> classPath
            ,Modifier fieldModifier,Modifier methodModifier) {
        this.init(javaPackage, subpackages, classPath, fieldModifier, methodModifier);
    }
    
    
    /**
     *     構造函數
     *     @param javaFilePath java文件地址,非空必填,絕對路徑
     *    @param classpath 源文件中引用的相關類的jar包地址,可選
     * */
    public JavaDocReader(String javaFilePath,List<String> classpath ) {
        this.init(javaFilePath, null, classpath, Modifier.PUBLIC, Modifier.PUBLIC);
    }
    
    
    
    
    /**
     *     構造函數
     *     @param javaFilePath java文件地址,非空必填,絕對路徑
     *    @param classpath 源文件中引用的相關類的jar包地址,可選
     *    @param fieldModifier 需要處理的類字段可見性,非空
     *    @param methodModifier 需要處理的類方法可見性,非空
     * */
    public JavaDocReader(String javaFilePath,List<String> classpath
            ,Modifier fieldModifier,Modifier methodModifier) {
        this.init(javaFilePath, null, classpath, fieldModifier, methodModifier);
    }
    
    
    
    
    /**
     *     構造函數
     *     @param sourcepath .java源文件地址,非空。可以是java源文件的絕對地址,也可以是包名。<br>
     *         如果是java源文件的絕對地址,只能填寫一個地址,不能填寫多個地址。<br>
     *         如果是包名,該包必須能在classpath下找到,只能填寫一個包名<br><br>
     *     @param classpath 類路徑,系統會在類路徑下去找一些相關的類文件,可以為空
     *    @param subpackages 是否遞歸處理子包,只有在sourcepath為包名時才起作用,可以為空
     *    @param fieldModifier 需要處理的類字段可見性,非空
     *    @param methodModifier 需要處理的類方法可見性,非空
     * */
    private void init(String sourcepath,String subpackages,List<String> classpath
            ,Modifier fieldModifier,Modifier methodModifier) {
        
        this.checkNotEmpty(sourcepath, "目標java文件不能為空");
        classpath = this.checkNotEmpty(classpath)?new LinkedList<String>(classpath):new LinkedList<String>();
        classpath.add(this.getJdkToolsJarPath());
        
        this.classpath = classpath;
        this.sourcepath = sourcepath;
        this.subpackages = subpackages;
        this.fieldModifier = fieldModifier == null?this.fieldModifier:fieldModifier;
        this.methodModifier = methodModifier == null?this.methodModifier:methodModifier;
        this.isSubpackages = this.checkNotEmpty(subpackages);
    }
    
    /**
     *     初始化參數
     *     @return String [] javadoc需要的參數數組
     * */
    private String [] initAgrs() {
        
        List<String> args = new LinkedList<String>();
        args.add("-encoding");
        args.add("utf-8");
        
        args.add("-doclet");
        args.add(MyDoclet.class.getName());
        
        args.add("-docletpath");
        args.add(MyDoclet.class.getResource("/").getPath());
        
        if(this.isSubpackages()) {
            args.add("-subpackages");
            args.add(this.getSubpackages());
        }
        
        StringBuilder sb = new StringBuilder();
        for(String classpath: this.getClasspath()) {
            sb.append(classpath).append(";");
        }
        if(sb.length() > 0) {
            sb.deleteCharAt(sb.length() - 1);
        }
        args.add("-classpath");
        args.add(sb.toString());
        
        if(this.fieldModifier == Modifier.PRIVATE || this.methodModifier == Modifier.PRIVATE) {
            args.add("-private");
        }else if(this.fieldModifier == Modifier.PROTECTED || this.methodModifier == Modifier.PROTECTED) {
            args.add("-protected");
        }else {
            args.add("-public");
        }
        
        args.add(this.sourcepath);
        
        return args.toArray(new String[args.size()]);
    }
    
    
    /**
     *     執行javadoc,解析源文件
     *     */
    private void executeJavadoc() {
        String[] initAgrs = this.initAgrs();
        com.sun.tools.javadoc.Main.execute(initAgrs);
    }
    
    
    /**
     *     獲取類注釋信息
     *     @return  List<ClassComment> 
     * */
    public List<ClassComment> execute(){
        this.executeJavadoc();
        RootDoc root = MyDoclet.getRoot();
        if(root == null) {
            return new LinkedList<ClassComment>();
        }
        RootClassParser parser = new RootClassParser(this.getFieldModifier(),this.getMethodModifier());
        List<ClassComment> parseResult = parser.parse(root);
        return parseResult;
    }
    
    
    
    public String getJdkToolsJarPath() {
        return jdkToolsJarPath;
    }


    public void setJdkToolsJarPath(String jdkToolsJarPath) {
        this.jdkToolsJarPath = jdkToolsJarPath;
    }


    public String getSubpackages() {
        return subpackages;
    }

    public void setSubpackages(String subpackages) {
        this.subpackages = subpackages;
    }

    public MyDoclet getDoclet() {
        return doclet;
    }

    public List<String> getClasspath() {
        return classpath;
    }

    public String getSourcepath() {
        return sourcepath;
    }

    public boolean isSubpackages() {
        return isSubpackages;
    }

    public Modifier getFieldModifier() {
        return fieldModifier;
    }

    public Modifier getMethodModifier() {
        return methodModifier;
    }

    public void setDoclet(MyDoclet doclet) {
        this.doclet = doclet;
    }

    public void setClasspath(List<String> classpath) {
        this.classpath = classpath;
    }

    public void setSourcepath(String sourcepath) {
        this.sourcepath = sourcepath;
    }

    public void setSubpackages(boolean isSubpackages) {
        this.isSubpackages = isSubpackages;
    }

    public void setFieldModifier(Modifier fieldModifier) {
        this.fieldModifier = fieldModifier;
    }

    public void setMethodModifier(Modifier methodModifier) {
        this.methodModifier = methodModifier;
    }



    
    
    


    @SuppressWarnings("rawtypes")
    private void checkNotEmpty(Object arg,String exceptionMsg) {
        if(exceptionMsg == null) {
            exceptionMsg = "參數不能為空。";
        }
        if(arg == null) {
            throw new NullPointerException(exceptionMsg);
        }
        if(arg instanceof String) {
            String argStr = (String)arg;
            if(argStr.isEmpty()) {
                throw new IllegalArgumentException(exceptionMsg);
            }
        }else if(arg instanceof Collection) {
            Collection collection = (Collection)arg;
            if(collection.isEmpty()) {
                throw new IllegalArgumentException(exceptionMsg);
            }
        }else if(arg instanceof Map) {
            Map map = (Map)arg;
            if(map.isEmpty()) {
                throw new IllegalArgumentException(exceptionMsg);
            }
        }
    }
    
    @SuppressWarnings("rawtypes")
    private boolean checkNotEmpty(Object arg) {
        if(arg == null) {
            return false;
        }
        if(arg instanceof String) {
            String argStr = (String)arg;
            if(argStr.isEmpty()) {
                return false;
            }
        }else if(arg instanceof Collection) {
            Collection collection = (Collection)arg;
            if(collection.isEmpty()) {
                return false;
            }
        }else if(arg instanceof Map) {
            Map map = (Map)arg;
            if(map.isEmpty()) {
                return false;
            }
        }
        return true;
    }
    
    
    
}

 

Test.java沒啥屁用的測試類,就是來看效果的
package com.hongcheng.javadoc_generator;

import java.util.Collections;
import java.util.List;

import com.hongcheng.javadoc_generator.entity.ClassComment;

public class Test {
    public static void main(String[] args) throws Exception {
        String jarPath = "C:\\Users\\HongCheng\\Desktop\\11.jar";
        JavaDocReader javaDocReader = new JavaDocReader( "hongcheng.code_generator"
                ,"hongcheng.code_generator"
                ,Collections.singletonList(jarPath),Modifier.PRIVATE,Modifier.PUBLIC );
        List<ClassComment> execute = javaDocReader.execute();
        
        WordExport wordExport = new WordExport();
        wordExport.export(execute,"C:\\Users\\HongCheng\\Desktop\\1.docx");
    }
}

 

這是javadoc的參數,你也可以自己設置

 

其實這個主要就是在代碼中使用javadoc這個工具,原理就是通過javadoc工具去解析目標類/jar,獲取到類信息和注釋信息后,在按照自己的格式去輸出

 

注意:

1、這個一般是你把你自己要導出文檔的項目打成jar包,再用這個工具進行處理,我沒試過把這個工具嵌入到當前項目中。

2、當然也可以指定一個java文件,但是它所依賴的一些jar你要加到classpath中。

3、打的jar包必須帶有java源碼,你可以打成源碼jar包。編譯后的jar是沒有注釋的。

4、這個的核心就是借助javadoc去解析java文件

 

代碼上傳碼雲了,相關jar包也在上面

https://gitee.com/1281003978/javadoc_generator

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM