前言
前一阵写了几个接口,需要给别人出一份接口文档。总觉手写挺麻烦的。看了一下接口入参的bean每个属性都有类似@notNull这样的注解,这种注解是用于接口调用时自动进行校验时用的。想了一下干脆通过扫描接口的参数的这些注解来生成一个文档。于是就写了一个工具类,来生成一个表格形式的接口文档。这个工具不需要启动spring,简单配置两三个参数,直接运行main方法就可以生成。
代码
用于扫描包的工具类:
package com.zbj.finance.member.api.excel;
import java.io.File;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
/**
*
* @author txf 扫描包的工具类
*/
public class ScannerUtils {
public static Set<String> scanBasePackage(String basePackageName) {
String packageDirName = basePackageName.replace(".", "/");
URL url = Thread.currentThread().getContextClassLoader()
.getResource(packageDirName);
System.out.println("文件路径:" + url);
File targetFile = new File(url.getFile());
if (!targetFile.exists() || targetFile.isFile()) {
throw new RuntimeException(basePackageName + "不是一个包名或者该包名不存在");
}
Set<String> classNames = new HashSet<String>();
getAllClass(targetFile, basePackageName, classNames);
return classNames;
}
/**
* 得到所有在parentFile目录下的class文件名称
*
* @param parentFile
* @param classNames
* @param basePackageName
*/
private static void getAllClass(File parentFile, String basePackageName,
Set<String> classNames) {
File[] files = parentFile.listFiles();
for (File file : files) {
String path = file.getPath();
if (file.isFile()) {
if (path.endsWith(".class")) {
classNames.add(basePackageName
+ "."
+ path.substring(path.lastIndexOf('\\') + 1,
path.lastIndexOf('.')));
}
} else {
basePackageName = basePackageName
+ path.substring(path.lastIndexOf('\\') + 1);
getAllClass(file, basePackageName, classNames);
}
}
}
}
用于生成Excel组装数据的对象:
package com.zbj.finance.member.api.excel;
import java.util.List;
/**
*
* @author txf
* 用于生成sheet的装入数据的对象
*/
public class ExcelSheet {
private String sheetName;
private String note;
private String classPath;
private String requestName;
private String responseName;
private List<String> head;
private List<List<String>> requestDatas;
private List<List<String>> responseDatas;
public String getRequestName() {
return requestName;
}
public void setRequestName(String requestName) {
this.requestName = requestName;
}
public String getResponseName() {
return responseName;
}
public void setResponseName(String responseName) {
this.responseName = responseName;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public String getClassPath() {
return classPath;
}
public void setClassPath(String classPath) {
this.classPath = classPath;
}
public String getSheetName() {
return sheetName;
}
public void setSheetName(String sheetName) {
this.sheetName = sheetName;
}
public List<String> getHead() {
return head;
}
public void setHead(List<String> head) {
this.head = head;
}
public List<List<String>> getRequestDatas() {
return requestDatas;
}
public void setRequestDatas(List<List<String>> requestDatas) {
this.requestDatas = requestDatas;
}
public List<List<String>> getResponseDatas() {
return responseDatas;
}
public void setResponseDatas(List<List<String>> responseDatas) {
this.responseDatas = responseDatas;
}
}
生成用于生成Excel的data对象:
package com.zbj.finance.member.api.excel;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.web.doc.annotation.ApidocElement;
import javax.web.doc.annotation.ApidocInterface;
import javax.web.doc.annotation.ApidocService;
/**
*
* @author txf
* 生成用于生成Excel的data对象
*
*/
public class ExcelDataCreater {
private String basePackageName;
public ExcelDataCreater(String basePackageName) {
super();
this.basePackageName = basePackageName;
}
public List<ExcelSheet> createExcelData(){
//扫描包
Set<String> classNames = ScannerUtils.scanBasePackage(basePackageName);
for(String str : classNames){
System.out.println("扫描到类:" + str);
}
//获取拥有对应注解的类
List<Class<?>> excelClass = new ArrayList<Class<?>>();
for(String str : classNames){
Class<?> c = null;
try {
c = Class.forName(str);
} catch (ClassNotFoundException e) {
System.out.println("获取类失败:" + str);
}
ApidocService apidocService = c.getAnnotation(ApidocService.class);
if(apidocService != null){
System.out.println("含注解@ApidocService的类:" + str);
excelClass.add(c);
}
}
//这里固定head的名称
List<String> head = new ArrayList<String>();
head.add("参数名");
head.add("类型");
head.add("是否必输");
head.add("长度");
head.add("描述");
List<ExcelSheet> ess = new ArrayList<ExcelSheet>();
for(Class<?> c : excelClass){
ApidocService apidocService = c.getAnnotation(ApidocService.class);
String classAnnotValue = apidocService.value();
//获取该类下所有包含@ApidocInterface注解的方法
for(Method method : c.getMethods()){
ApidocInterface apidocInterface = method.getAnnotation(ApidocInterface.class);
if(apidocInterface != null){
String methodAnnotValue = apidocInterface.value();
ExcelSheet es = new ExcelSheet();
//放入固定值head
es.setHead(head);
//类名 + 方法名 = 一个sheet名
es.setSheetName(c.getSimpleName() + "." + method.getName());
//获取说明
String note = classAnnotValue + "——" + methodAnnotValue;
es.setNote(note);
//获取类名
String classPath = c.getName();
es.setClassPath(classPath + "." + method.getName());
//获取方法参数对象
Class<?>[] parameters = method.getParameterTypes();
//目前接口就一个对象,所以获取第一个
Class<?> parrameterClass = parameters[0];
//放入请求参数的内容
es.setRequestName(parrameterClass.getName());
es.setRequestDatas(createDatas(parrameterClass));
//放入返回参数的内容
Class<?> responseClass = method.getReturnType();
es.setResponseName(responseClass.getName());
es.setResponseDatas(createDatas(responseClass));
ess.add(es);
}
}
}
return ess;
}
private List<List<String>> createDatas(Class<?> c){
List<List<String>> datas = new ArrayList<List<String>>();
for(Field filed : c.getDeclaredFields()){
//只记录有@ApidocElement注解的属性
ApidocElement apidocElement = filed.getAnnotation(ApidocElement.class);
if(apidocElement != null){
//一个属性的详细信息对应一个list
List<String> data = new ArrayList<String>();
data.add(filed.getName());
data.add(filed.getType().getSimpleName());
data.add(String.valueOf(apidocElement.required()));
data.add(apidocElement.minLen() + "-" + apidocElement.maxLen());
data.add(apidocElement.value());
datas.add(data);
}
}
return datas;
}
}
生成Excel的工具类:
package com.zbj.finance.member.api.excel;
import org.apache.poi.hssf.util.HSSFColor;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.util.CellRangeAddress;
/**
* Created by txf
* 生成Excel的工具
*/
public class ExcelCreater {
public static void createExcel(List<ExcelSheet> sheets, String filePath, String fileName) throws Exception{
HSSFWorkbook book = new HSSFWorkbook();
for(ExcelSheet excelSheet : sheets){
createSheet(book, excelSheet);
}
creatFile(filePath, fileName);
File file = new File(filePath + "/" +fileName + ".xls");
OutputStream os = null;
os = new FileOutputStream(file);
book.write(os);
}
private static void createSheet(HSSFWorkbook book, ExcelSheet excelSheet){
try {
HSSFSheet sheet = book.createSheet(excelSheet.getSheetName());
HSSFCellStyle style = book.createCellStyle();
HSSFFont font = book.createFont();
setFont(font);
setStyle(style, font);
// 加载合并的说明行第一行;起始行,结束行,起始列,结束列
CellRangeAddress callRangeAddress = new CellRangeAddress(0,0,1,excelSheet.getHead().size() - 1);
sheet.addMergedRegion(callRangeAddress);
sheet.addMergedRegion(callRangeAddress);
sheet.addMergedRegion(callRangeAddress);
//第一行是说明
HSSFRow row0 = sheet.createRow(0);
row0.createCell(0).setCellValue("说明:");
row0.createCell(1).setCellValue(excelSheet.getNote());
//第二行是接口名
HSSFRow row1 = sheet.createRow(1);
row1.createCell(0).setCellValue("接口名称:");
row1.createCell(1).setCellValue(excelSheet.getClassPath());
//内容开始行
int dataIndex = 3;
//内容的第一行是,请求参数名
sheet.addMergedRegion(callRangeAddress);
HSSFRow row2 = sheet.createRow(dataIndex);
row2.createCell(0).setCellValue("请求参数名:");
row2.createCell(1).setCellValue(excelSheet.getRequestName());
dataIndex++;
//填充表头header
HSSFRow row = sheet.createRow(dataIndex);
int headIndex = 0;
for(String headStr : excelSheet.getHead()){
HSSFCell cell = row.createCell(headIndex);
cell.setCellValue(headStr);
headIndex++;
}
int dataRow = dataIndex + 1;
for(List<String> datas : excelSheet.getRequestDatas()){
HSSFRow rowcol = sheet.createRow(dataRow);//index:第几行
//定义列数
int dataCol = 0;
for(String str : datas){
HSSFCell cell = rowcol.createCell(dataCol);//第几列:从0开始
cell.setCellValue(str);
dataCol++;
}
dataRow++;
}
dataRow++;
//写入返回参数
sheet.addMergedRegion(callRangeAddress);
HSSFRow row3 = sheet.createRow(dataRow);
row3.createCell(0).setCellValue("返回参数名:");
row3.createCell(1).setCellValue(excelSheet.getResponseName());
dataRow++;
//填充表头header
HSSFRow rowTow = sheet.createRow(dataRow);
int headIndex2 = 0;
for(String headStr : excelSheet.getHead()){
HSSFCell cell = rowTow.createCell(headIndex2);
cell.setCellValue(headStr);
headIndex2++;
}
dataRow++;
for(List<String> datas : excelSheet.getResponseDatas()){
HSSFRow rowcol = sheet.createRow(dataRow);//index:第几行
//定义列数
int dataCol = 0;
for(String str : datas){
HSSFCell cell = rowcol.createCell(dataCol);//第几列:从0开始
cell.setCellValue(str);
dataCol++;
}
dataRow++;
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void setStyle(HSSFCellStyle style, HSSFFont font) {
//样式设置
style.setFillForegroundColor(HSSFColor.SKY_BLUE.index);
style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
style.setBorderRight(HSSFCellStyle.BORDER_THIN);
style.setBorderTop(HSSFCellStyle.BORDER_THIN);
style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
style.setFont(font);
}
private static void setFont(HSSFFont font) {
font.setColor(HSSFColor.VIOLET.index);
font.setFontHeightInPoints((short) 12);
font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
}
private static void creatFile(String filePath, String fileName) {
File folder = new File(filePath);
//文件夹路径不存在
if (!folder.exists() && !folder.isDirectory()) {
System.out.println("文件夹路径不存在,创建路径:" + filePath);
folder.mkdirs();
} else {
System.out.println("文件夹路径存在:" + filePath);
}
// 如果文件不存在就创建
File file = new File(filePath + fileName);
if (!file.exists()) {
System.out.println("文件不存在,创建文件:" + filePath + fileName);
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
} else {
System.out.println("文件已存在,文件为:" + filePath + fileName);
}
}
}
main方法:
package com.zbj.finance.member.api.excel;
/**
*
* @author txf
* 直接运行,生成Excel文档
*/
public class CreateExcelFile {
public static void main(String[] args) throws Exception {
ExcelDataCreater edc = new ExcelDataCreater("com.xxx.xxxx.xxx.api.service");
ExcelCreater.createExcel(edc.createExcelData(), "C:/excel", "接口文档");
}
}
其中一个用于接口入参的bean:
package com.zbj.finance.member.api.domains;
import javax.web.doc.AbstractResponse;
import javax.web.doc.annotation.ApidocElement;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import com.zbj.finance.member.api.domains.BalanceResponse.BalanceResponseBuilder;
import com.zbj.finance.member.api.enums.BusinessStatus;
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class DrawMoneyRequest extends AbstractResponse{
@ApidocElement(value = "主网用户编号",minLen = 1, maxLen = 11)
Integer userId;
@ApidocElement(value = "产品业务代码",minLen = 1, maxLen = 36)
String bizCode;
@ApidocElement(value = "订单标题",minLen = 1, maxLen = 255)
String title;
@ApidocElement(value = "外部订单号",minLen = 1, maxLen = 100)
String orderID;
@ApidocElement(value = "放款金额",minLen = 1, maxLen = 9)
String amount;
@ApidocElement(value = "银行卡号",minLen = 1, maxLen = 25)
String bankAccountNo;
}