刚刚上次弄完了一个坑爹的任务,这次我领导又给我一个让人脑瓜子疼的任务了。

基本上客户他在验收我们系统的时候,都会要求我们编写相关的文档,这次也不例外。

只是这次的客户要求我们给出接口文档。不仅是要整个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、调用com.sun.tools.javadoc.Main.execute()这个方法,传入参数

 2、写一个自己的Doclet类。

 3、拿到数据后,自己解析class类型、方法名、类名、方法注释、类注释、字段注释、参数注释............然后爱生成HTML、爱生成Word、爱生成Excel,随你们喜欢。

大概怎么用说完了,展示下我的生成后的效果吧,我没调样式,毕竟用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的参数,你也可以自己设置

代码上传码云了,相关jar包也在上面

https://gitee.com/1281003978/javadoc_generator

javadoc导出成word文档的更多相关文章

  1. 网页导出成word文档的默认视图方式问题

    网页导出成word文档的默认视图方式问题 一般保存后的word文档默认是“Web版式视图”打开,这样会给客户的感觉不是真正的word文档,必须实现打开就是“页面视图” 1. 修改<html> ...

  2. php将html转成word文档下载

    <meta charset="utf-8" /> <?php class word{ function start(){ ob_start(); echo '&l ...

  3. ABBYY将JPEG文件转换成Word文档的方法

    日常工作中处理JPEG格式的图像文件时,有时需要转换成Word文档进行编辑,市场上应用而生了很多转换工具,相信不少人听说过OCR(光学字符识别)软件,可以用来转换图像文件,而在OCR软件中, ABBY ...

  4. PowerDesigner将PDM导出生成WORD文档

    PowerDesigner将PDM导出生成WORD文档 环境 PowerDesigner15 1.点击Report Temlates 制作模板 2.如果没有模板,单击New图标创建.有直接双击进入. ...

  5. 将HTML导出生成word文档

    前言: 项目开发中遇到了需要将HTML页面的内容导出为一个word文档,所以有了这边随笔. 当然,项目开发又时间有点紧迫,第一时间想到的是用插件,所以百度了下.下面就介绍两个导出word文档的方法. ...

  6. 如何使用ABBYY FineReader 12将JPEG文件转换成Word文档

    日常工作中处理JPEG格式的图像文件时,有时需要转换成Word文档进行编辑,市场上应用而生了很多转换工具,相信不少人听说过OCR(光学字符识别)软件,可以用来转换图像文件,而在OCR软件中, ABBY ...

  7. (转)WEB页面导出为Word文档后分页&横向打印的方法

    <html>    <HEAD>        <title>WEB页面导出为Word文档后分页&横向打印的方法 </title>    < ...

  8. 可以把思维导图导出为word文档方便其他人查看吗?

    MindManager除了强大的大纲视图编辑功能外,还拥有多种导出格式,方便大家迅速导出文件,在团队中分享自己的观点,提高团队的工作效率,本次小编使用的思维导图软件版本是MindManager 202 ...

  9. C# : 操作Word文件的API - (将C# source中的xml注释转换成word文档)

    这篇博客将要讨论的是关于: 如何从C#的source以及注释, 生成一份Word格式的关于各个类,函数以及成员变量的说明文档. 他的大背景如下...... 最近的一个项目使用C#, 分N个模块, 在项 ...

随机推荐

  1. AVIRIS 简介

    AVIRIS 是指 机载可见光近红外成像光谱(Airborne Visible InfraRed Imaging Spectrometer).是由美国NASA下属的喷气动力实验室(JPL)开发和维护的 ...

  2. Ratel源码-C/S事件梳理

    一.Ratel介绍 Ratel 是一个可以在命令行中玩斗地主的项目,可以使用小巧的jar包在拥有JVM环境的终端中进行游戏,同时支持人人对战和人机对战两种模式,丰富你的空闲时间! 二.玩法Demo 三 ...

  3. 报错:The server cannot be started because one or more of the ports are invalid. Open the server editor and correct the invalid ports.

    今天重装eclipse和Tomcat,启动时候报标题错“The server cannot be started because one or more of the ports are invali ...

  4. Rocket - util - LanePositionedQueue

    https://mp.weixin.qq.com/s/yO_9Ec3S5-AosRVLpsBgOg   简单介绍基于通道位置的队列(LanePositionedQueue)的实现.   ​​   1. ...

  5. Rocket - decode - Term

    https://mp.weixin.qq.com/s/CbBWdTlc_DESlUzd8KFcSg   介绍Term类各方法的实现.     ​​   1. prime   表示一个项是否是质项,即这 ...

  6. Java实现 LeetCode 396 旋转函数

    396. 旋转函数 给定一个长度为 n 的整数数组 A . 假设 Bk 是数组 A 顺时针旋转 k 个位置后的数组,我们定义 A 的"旋转函数" F 为: F(k) = 0 * B ...

  7. Java实现 蓝桥杯VIP 算法训练 筛选号码

    算法训练 筛选号码 Description 有n个人围成一圈,顺序排号(编号为1到n).从第1个人开始报数(从1到3报数),凡报到3的人退出圈子.从下一个人开始继续报数,直到剩下最后一个人,游戏结束. ...

  8. Java实现 蓝桥杯VIP 算法训练 整数平均值

    题目描述 编写函数,求包含n个元素的整数数组中元素的平均值.要求在函数内部使用指针操纵数组元素,其中n个整数从键盘输入,输出为其平均值. (样例说明:5为输入数据的个数,3 4 0 0 2 是以空格隔 ...

  9. Java实现 LeetCode 171 Excel表列序号

    171. Excel表列序号 给定一个Excel表格中的列名称,返回其相应的列序号. 例如, A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> ...

  10. Android中StateListDrawable的种类(状态的种类)

    <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="ht ...