项目中需要写API接口文档。
目前已经使用过swagger 但是swagger具有局限性
1、现网不可能开放swagger
2、接口没有具体说明,返回参数没用说明属性
这边写了了一个自定义扫描的。
使用场景:
1、扫描包下的controller注解类
2、扫描controller类型下RequestMapping注解的方法
3、获取方法的请求参数类,并获取参数类中的非静态参数
4、获取方法的返回参数,并获取参数类中的非静态参数(尚未实现)
可以生成以下文档
1[first]
---
###1.1接口说明[first]
|包名|类名|方法名|参数名|
|-----------|-----------|-----------|-----------|
|com.test|TestController|queryFirst|QueryFirstRequest|
###1.2输入参数
|名称|类型
|-----------|-----------|
|name|String|
|first|String|
1[first]
1.1接口说明[first]
包名 | 类名 | 方法名 | 参数名 |
---|---|---|---|
com.test | TestController | queryFirst | QueryFirstRequest |
1.2输入参数
名称 | 类型 |
---|---|
name | String |
first | String |
实现代码
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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.test</groupId>
<artifactId>annotation-reflect-test</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
</dependencies>
</project>
package com.test;
import java.lang.reflect.Method;
import java.util.List;
public class ExecutorBean {
//方法
private Method method;
//controller类的mapping
private String classMapping;
//类所在包名
private String packageName;
//类名
private String className;
//请求参数名称
private String paramClassName;
//方法的url
private String url;
//请求参数类的请求参数
private List<String> paramNameList;
//请求参数类的请求参数对象
private List<ParamVO> paramVOList;
public List<ParamVO> getParamVOList() {
return paramVOList;
}
public void setParamVOList(List<ParamVO> paramVOList) {
this.paramVOList = paramVOList;
}
public List<String> getParamNameList() {
return paramNameList;
}
public void setParamNameList(List<String> paramNameList) {
this.paramNameList = paramNameList;
}
public String getUrl() {
return url;
}
@Override
public String toString() {
return "ExecutorBean{" +
"method=" + method +
", classMapping='" + classMapping + '\'' +
", packageName='" + packageName + '\'' +
", className='" + className + '\'' +
", paramClassName='" + paramClassName + '\'' +
", url='" + url + '\'' +
", paramNameList=" + paramNameList +
", paramVOList=" + paramVOList +
'}';
}
public void setUrl(String url) {
this.url = url;
}
public String getParamClassName() {
return paramClassName;
}
public void setParamClassName(String paramClassName) {
this.paramClassName = paramClassName;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public String getClassMapping() {
return classMapping;
}
public void setClassMapping(String classMapping) {
this.classMapping = classMapping;
}
public String getPackageName() {
return packageName;
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
}
package com.test;
public class ParamVO {
//参数名
private String paramName;
//参数类型
private String paramType;
public String getParamName() {
return paramName;
}
public void setParamName(String paramName) {
this.paramName = paramName;
}
public String getParamType() {
return paramType;
}
public void setParamType(String paramType) {
this.paramType = paramType;
}
@Override
public String toString() {
return "ParamVO{" +
"paramName='" + paramName + '\'' +
", paramType='" + paramType + '\'' +
'}';
}
}
package com.test;
import org.reflections.Reflections;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import static javafx.scene.input.KeyCode.O;
public class AnnoManageUtil {
public static Map<String, ExecutorBean> getRequestMappingMethod(Class classes) {
Map<String, ExecutorBean> mapp = new HashMap<String, ExecutorBean>();
Method[] methods = classes.getDeclaredMethods();
RequestMapping requestMappingClass = (RequestMapping) classes.getAnnotation(RequestMapping.class);
String classMappValue = "";
try {
String[] requestMappingClassValue = requestMappingClass.value();
classMappValue = requestMappingClassValue[0];
} catch (Exception e) {
classMappValue = "";
}
for (Method method : methods) {
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
if (null != requestMapping) {
ExecutorBean executorBean = new ExecutorBean();
try {
executorBean.setClassMapping(classMappValue);
executorBean.setMethod(method);
executorBean.setPackageName(classes.
getPackage().getName());
executorBean.setClassName(classes.getSimpleName());
} catch (Exception e) {
e.printStackTrace();
}
String key = Arrays.toString(requestMapping.value());
if (StringUtils.isEmpty(key)) {
continue;
}
executorBean.setMethod(method);
mapp.put(Arrays.toString(requestMapping.value()).replaceFirst("/", ""), executorBean);
}
}
return sortMapByKey(mapp);
}
public static Map<String, ExecutorBean> sortMapByKey(Map<String, ExecutorBean> map) {
Map<String, ExecutorBean> sortedMap = new TreeMap<String, ExecutorBean>(new Comparator<String>() {
public int compare(String keyl, String key2) {
int flag = keyl.compareTo(key2);
int flag2 = 0;
if (flag > 0) {
flag2 = -1;
} else if (flag < 0) {
flag2 = 1;
}
return flag2;
}
});
sortedMap.putAll(map);
return sortedMap;
}
public static Set<Class<?>> getPackageController(String packageName, boolean subPackageFlag) {
Set<String> packageNametist = new HashSet<String>();
packageNametist.add(packageName);
if (subPackageFlag) {
packageNametist.addAll(getPackageList(packageName));
}
Set<Class<?>> classesList = new HashSet<Class<?>>();
for (String p : packageNametist) {
classesList.addAll(getPackageController(p));
}
return classesList;
}
public static Set<Class<?>> getPackageController(String packageName) {
Reflections reflections = new Reflections(packageName);
Set<Class<?>> classeslist = reflections.getTypesAnnotatedWith(Controller.class);
return classeslist;
}
public static Class<?> getParamByMethod(Method method) {
Set<Class<?>> paramNameSet = new HashSet<Class<?>>();
Class<?>[] parameterTypes = method.getParameterTypes();
for (Class<?> clas : parameterTypes) {
String parameterName = clas.getName();
if (parameterName.contains("HttpServletRequest")) {
continue;
}
paramNameSet.add(clas);
return clas;
}
return null;
}
private static Set<String> getPackageByFile(String packagePath, Set<String> packageNameList) {
File file = new File(packagePath);
File[] childFiles = file.listFiles();
for (
File childFile : childFiles) {
if (childFile.isDirectory()) {
packageNameList.add(childFile.getAbsolutePath());
getPackageByFile(childFile.getAbsolutePath(), packageNameList);
}
}
return packageNameList;
}
public static Set<String> getPackageList(String packageName) {
Set<String> fileNames = new HashSet<String>();
ClassLoader loader =
Thread.currentThread().getContextClassLoader();
String packagePath = packageName.replace(".", "/");
URL url = loader.getResource(packagePath);
if (url != null) {
String type = url.getProtocol();
if (type.equals("file")) {
getPackageByFile(url.getPath(), fileNames);
}
}
String path = url.getPath().replaceFirst("/", "").replace("/", ".");
Set<String> filePathSet = new HashSet<String>();
for (String str : fileNames) {
str = str.replace("\\", ".");
String packageEnd = str.replace(path, "");
filePathSet.add(packageName + packageEnd);
}
return filePathSet;
}
public static List<String> getParamByClass(Class paramClass) {
if (paramClass == null) {
return new ArrayList<String>();
}
List<String> paramList = new ArrayList<String>();
Field[] fields = paramClass.getDeclaredFields();
try {
for (Field field : fields) {
String fieldName = field.getName();
if (fieldName.contains("serialVersionUlD")) {
continue;
}
if ((field.getModifiers() & java.lang.reflect.Modifier.STATIC) == java.lang.reflect.Modifier.STATIC) {
continue;
}
paramList.add(fieldName);
}
} catch (Exception e) {
e.printStackTrace();
}
return paramList;
}
public static List<ParamVO> getParamVOByClass(Class paramClass) {
if (paramClass == null) {
return new ArrayList<ParamVO>();
}
List<ParamVO> paramVOList = new ArrayList<ParamVO>();
Field[] fields = paramClass.getDeclaredFields();
try {
for (Field field : fields) {
String fieldName = field.getName();
if (fieldName.contains("serialVersionUlD")) {
continue;
}
if ((field.getModifiers() & java.lang.reflect.Modifier.STATIC) == java.lang.reflect.Modifier.STATIC) {
continue;
}
ParamVO vo = new ParamVO();
vo.setParamType(field.getType().getSimpleName());
vo.setParamName(fieldName);
paramVOList.add(vo);
}
} catch (Exception e) {
e.printStackTrace();
}
return paramVOList;
}
public static void print(String info, Object... args) {
System.out.println(String.format(info, args));
}
}
package com.test;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 1、扫描包下的controller注解类
* 2、扫描controller类型下RequestMapping注解的方法
* 3、获取方法的请求参数类,并获取参数类中的非静态参数
*/
public class ApiWikiTest {
public static void main(String[] args) {
//获取包下的controller类
Set<Class<?>> classList = AnnoManageUtil.getPackageController("com.test", true);
List<ExecutorBean> beanList = new ArrayList<ExecutorBean>();
for (Class cls : classList) {
//获取类下的requestMappinp方法
Map<String, ExecutorBean> map = AnnoManageUtil.getRequestMappingMethod(cls);
if (map == null) {
continue;
}
for (String key : map.keySet()) {
ExecutorBean bean = map.get(key);
//获取方法的请求参数类
Class paramClass = AnnoManageUtil.getParamByMethod(bean.getMethod());
if (paramClass != null) {
bean.setParamClassName(paramClass.getSimpleName());
}
//获取请求参数类的具体参数
List<String> paramNameList = AnnoManageUtil.getParamByClass(paramClass);
List<ParamVO> paramVOList = AnnoManageUtil.getParamVOByClass(paramClass);
bean.setParamNameList(paramNameList);
bean.setParamVOList(paramVOList);
bean.setUrl(key);
beanList.add(bean);
}
}
int i = 1;
//输出wiki文档
for (ExecutorBean bean : beanList) {
int num = i++;
String url = bean.getUrl();
String packageName = bean.getPackageName();
List<String> paramNameList = bean.getParamNameList();
List<ParamVO> paramVOList = bean.getParamVOList();
Method method = bean.getMethod();
AnnoManageUtil.print(num + "%s", url);
AnnoManageUtil.print("---");
AnnoManageUtil.print("###%s.1接口说明%s", num + "", url);
AnnoManageUtil.print("|包名|类名|方法名|参数名|");
AnnoManageUtil.print("|-----------|-----------|-----------|-----------|");
AnnoManageUtil.print("|%s|%s|%s|%s|", packageName, bean.getClassName(),
method.getName(), bean.getParamClassName());
AnnoManageUtil.print("###%s.2输入参数", num + "");
AnnoManageUtil.print("|名称|类型");
AnnoManageUtil.print("|-----------|-----------|");
for (ParamVO paramVO : paramVOList) {
AnnoManageUtil.print("|%s|%s|", paramVO.getParamName(), paramVO.getParamType());
}
}
}
}
测试请求对象
package com.test.request;
public class QueryFirstRequest {
private String name;
private String first;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getFirst() {
return first;
}
public void setFirst(String first) {
this.first = first;
}
@Override
public String toString() {
return "QueryFirstRequest{" +
"name='" + name + '\'' +
", first='" + first + '\'' +
'}';
}
}
测试controller
package com.test;
import com.test.request.QueryFirstRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping(value="/test")
public class TestController {
@RequestMapping(value="/first")
public void queryFirst(QueryFirstRequest rqeuest){
}
}