问题:
由于项目技术无法使用swagger接口层扫描技术,因此需要简易开发一段req,rep实体字段类型生成描述工具类。
结果示例如下:
1:
{
"outBill": "String",
"collectWhCode": "String",
"boxNum": "String",
"boxList": [{
"boxTid": "String",
"boxEpc": "String",
"barcode": "String",
"collectTime": "String",
"deviceId": "String",
"spec": "String",
}],
"userAccount": "String",
"token": "String",
}
2:
{
"list": [{
"bill": "String",
"teamNo": "String",
"time": "String",
"planNum": "int",
"countBack4": "int",
"countFront4": "int",
}],
"page": "Page",
"statusCode": "StatusCode",
"message": "String",
"rep": "String",
}
工具类主程序代码:
import java.io.File;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.annotation.WebServlet;
/**
* @ClassName: PrintJson
* @Description: TODO(这里用一句话描述这个类的作用)
* @author:
* @date: 2020-7-22 下午4:09:18
* @Copyright: 2020 www.techsun.com.cn Inc. All rights reserved.
*/
public class PrintJson {
/**
* @Title: main
* @Description: TODO(用一句话描述该文件做什么)
* @author:
* @version: V1.0
* @Date: 2020-7-22 下午4:09:18
* @param args
*/
public static void main(String[] args) {
try {
/**
* 方式1:实现单个对象的请求响应体的json扫描
*/
// UploadOutRridBoxInfoReq req = new UploadOutRridBoxInfoReq();
// System.out.println(objList2Json(req));
//-----------------------------------------------------------------------------------------------------------
/**
* 方式2:实现批量对象的请求响应体的json扫描
* 实现方式:通过指定包名实现包下所有类的扫描,以及每个类中所有方法的请求响应扫描
* 注意:扫描之前需要在对应类接口方法中添加指定的注解,实现接口请求响应对象(类的全路径)的获取
* 请求响应对象扫描注解:@MethodJson(methodRep = "com.techsun.mttrace.servlet.base.rep.BaseJsonRep", methodReq = "com.techsun.mttrace.servlet.auth.req.AppLoginReq")
*/
List<JsonResult> list = objListUrl("com.techsun.mttrace.servlet.produce");
//将数据写入到本地文本中
PrintWriter pw=new PrintWriter(new File("C:\\Users\\Administrator\\Desktop\\json.txt"),"UTF-8");
for(JsonResult result : list) {//写出的行数
pw.append("url:"+result.getUrl()+"\treq:"+result.getReqJson()+"\trep:"+result.getRepJson()+"\n");
}
pw.append("-------------------------接口个数为:"+ list.size() +"------------------------");
System.out.println("文件生成结束!");
pw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 扫描指定路径下所有接口路径
* @return
* @throws Exception
*/
public static List<JsonResult> objListUrl(String packageName) throws Exception {
ClasspathPackageScanner scan = new ClasspathPackageScanner(packageName);
List<String> listClass = scan.getFullyQualifiedClassNameList();
List<JsonResult> list = new ArrayList<JsonResult>();
JsonResult jsonResult = null;
Map<String,Method> methodMap = new HashMap<String,Method>();
WebServlet ws;
String url;
for(String className : listClass) {
ws = Class.forName(className).getAnnotation(WebServlet.class);
methodMap = scan.getMethod(className);
if(null != ws && ws.value().length > 0) {
Iterator<Map.Entry<String, Method>> entries = methodMap.entrySet().iterator();
while(entries.hasNext()) {
jsonResult = new JsonResult();
Map.Entry<String, Method> entry = entries.next();
String methodName = entry.getKey();//方法名称string
Method method = entry.getValue();
if(methodName.equals("doGet") || methodName.equals("doPost")) {
continue;
}
url = "http://192.168.1.65:9080/mttraceWEB";
url += ws.value()[0].replace("*", "") + methodName;//扫描得到接口路径
jsonResult.setUrl(url);//设置接口路径
MethodJson mj = method.getAnnotation(MethodJson.class);
if(null != mj) {
String rep = mj.methodRep();
String req = mj.methodReq();
Object objRep = Class.forName(rep).newInstance();
Object objReq = Class.forName(req).newInstance();
String jsonRep = objList2Json(objRep);
String jsonReq = objList2Json(objReq);
jsonResult.setRepJson(jsonRep);
jsonResult.setReqJson(jsonReq);
}
list.add(jsonResult);
}
}
}
return list;
}
/**
* 扫描实体类返回json格式字符串
* @param obj
* @return
* @throws Exception
*/
public static String objList2Json(Object obj) throws Exception {
String json = "{";
Field[] fds = obj.getClass().getDeclaredFields();
for(Field fd : fds) {//本类字段类型扫描
// System.out.println(fd.getType().toString());
String fdName = fd.getName();
if(fd.getType().toString().contains("List")) {//泛型
String fieldTypeName = fd.getGenericType().getTypeName();
json += "\""+ fd.getName() + "\" : [";
String className = fieldTypeName.substring(fieldTypeName.indexOf('<')+1, fieldTypeName.indexOf('>'));
Class<?> cl = Class.forName(className);
json = json + objList2Json(cl.newInstance()) +"],";
}else if(fd.getType().toString().contains("com.techsun.mttrace")
&& !fd.getType().getSuperclass().toString().contains("Enum")) {//扫描类中自定义实体类字段属性名称
json += "\""+ fd.getName() + "\" : ";
json = json + objList2Json(fd.getType().newInstance()) +",";
}else {
json += "\"" + fdName + "\" : " +"\""+ fd.getType().getSimpleName() +"\",";
}
}
String className1 = "";
if(null != obj.getClass().getSuperclass() && !obj.getClass().getSuperclass().equals(Object.class)) {//上一层父类字段类型扫描
Field[] fieldSups = obj.getClass().getSuperclass().getDeclaredFields();
for(Field fdSup : fieldSups) {
String fdSupName = fdSup.getName();
if(!fdSup.getType().toString().contains("List")) {
json += "\"" + fdSupName + "\" : " +"\""+ fdSup.getType().getSimpleName() +"\",";
}else {//泛型
String fieldSupTypeName = fdSup.getGenericType().getTypeName();
json += "\""+ fdSup.getName() + "\" : [";
String className = fieldSupTypeName.substring(fieldSupTypeName.indexOf('<')+1, fieldSupTypeName.indexOf('>'));
Class<?> cl = null;
if(className.contains("T")) {
// cl = getSuperClassGenericType(obj.getClass(),0);
className = obj.getClass().getGenericSuperclass().toString().split("<")[1].replace(">", "");
className1 = obj.getClass().getGenericSuperclass().toString().split("<")[0];
}
cl = Class.forName(className);
json = json + objList2Json(cl.newInstance()) +"],";
}
}
}
if(!className1.equals("")) {//扫描二级父类-----------------------------
Object classObj = Class.forName(className1).newInstance();
if(!classObj.getClass().getSuperclass().equals(Object.class)) {
Field[] fieldSup2s = classObj.getClass().getSuperclass().getDeclaredFields();
for(Field fdSup2 : fieldSup2s) {
String fdSup2Name = fdSup2.getName();
if(!fdSup2.getType().toString().contains("List")) {
json += "\"" + fdSup2Name + "\" : " +"\""+ fdSup2.getType().getSimpleName() +"\",";
}
}
}
}
json += "}";
return json;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public static Class<Object> getSuperClassGenericType(Class<?> clazz,int index) {
//getSuperclass()获得该类的父类
//getGenericSuperclass()获得带有泛型的父类
//Type是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。
Type genType= clazz.getGenericSuperclass();//获取带泛型参数的父类
if(!(genType instanceof ParameterizedType)){
return Object.class;
}
Type[] param = ((ParameterizedType) genType).getActualTypeArguments();//获取真实泛型参数
if(index >= param.length || index < 0){
return Object.class;
}
if(!(param[index] instanceof Class)){
return Object.class;
}
return (Class) param[index];
}
}
配置类代码,指定包名路径,扫描包下所有类的全名,以及获取每个类中的方法名称:
工具类:
public class StringUtil {
public static String getRootPath(URL url) {
String fileUrl = url.getFile();
int pos = fileUrl.indexOf('!');
if (-1 == pos) {
return fileUrl;
}
return fileUrl.substring(5, pos);
}
/**
*
* @param name
* @return
*/
public static String dotToSplash(String name) {
return name.replaceAll("\\.", "/");
}
/**
*
* @param name
* @return
*/
public static String trimExtension(String name) {
int pos = name.indexOf('.');
if (-1 != pos) {
return name.substring(0, pos);
}
return name;
}
/**
*
* @param uri
* @return
*/
public static String trimURI(String uri) {
String trimmed = uri.substring(1);
int splashIndex = trimmed.indexOf('/');
return trimmed.substring(splashIndex);
}
}
注解定义:
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ java.lang.annotation.ElementType.METHOD })
@Documented
public @interface MethodJson {
//响应对象
String methodRep();
//请求对象
String methodReq();
}
扫描包执行类:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ClasspathPackageScanner {
static class StringUtil {
public static String getRootPath(URL url) {
String fileUrl = url.getFile();
int pos = fileUrl.indexOf('!');
if (-1 == pos) {
return fileUrl;
}
return fileUrl.substring(5, pos);
}
/**
*
* @param name
* @return
*/
public static String dotToSplash(String name) {
return name.replaceAll("\\.", "/");
}
/**
*
* @param name
* @return
*/
public static String trimExtension(String name) {
int pos = name.indexOf('.');
if (-1 != pos) {
return name.substring(0, pos);
}
return name;
}
/**
*
* @param uri
* @return
*/
public static String trimURI(String uri) {
String trimmed = uri.substring(1);
int splashIndex = trimmed.indexOf('/');
return trimmed.substring(splashIndex);
}
}
private Logger logger = LoggerFactory.getLogger(ClasspathPackageScanner.class);
private String basePackage;
private ClassLoader cl;
public ClasspathPackageScanner(String basePackage) {
this.basePackage = basePackage;
this.cl = getClass().getClassLoader();
}
public ClasspathPackageScanner(String basePackage, ClassLoader cl) {
this.basePackage = basePackage;
this.cl = cl;
}
public List<String> getFullyQualifiedClassNameList() throws IOException {
logger.info("开始扫描包{}下的所有类", basePackage);
return doScan(basePackage, new ArrayList<String>());
}
private List<String> doScan(String basePackage, List<String> nameList) throws IOException {
String splashPath = StringUtil.dotToSplash(basePackage);
URL url = cl.getResource(splashPath);
String filePath = StringUtil.getRootPath(url);
List<String> names = null; // contains the name of the class file. e.g., Apple.class will be stored as "Apple"
if (isJarFile(filePath)) {
// jar file
if (logger.isDebugEnabled()) {
// logger.debug("{} 是一个JAR包", filePath);
}
names = readFromJarFile(filePath, splashPath);
} else {
// directory
if (logger.isDebugEnabled()) {
// logger.debug("{} 是一个目录", filePath);
}
names = readFromDirectory(filePath);
}
for (String name : names) {
if (isClassFile(name)) {
//nameList.add(basePackage + "." + StringUtil.trimExtension(name));
nameList.add(toFullyQualifiedName(name, basePackage));
} else {
doScan(basePackage + "." + name, nameList);
}
}
// if (logger.isDebugEnabled()) {
// for (String n : nameList) {
// logger.debug("找到{}", n);
// }
// }
return nameList;
}
private String toFullyQualifiedName(String shortName, String basePackage) {
StringBuilder sb = new StringBuilder(basePackage);
sb.append('.');
sb.append(StringUtil.trimExtension(shortName));
return sb.toString();
}
private List<String> readFromJarFile(String jarPath, String splashedPackageName) throws IOException {
if (logger.isDebugEnabled()) {
// logger.debug("从JAR包中读取类: {}", jarPath);
}
@SuppressWarnings("resource")
JarInputStream jarIn = new JarInputStream(new FileInputStream(jarPath));
JarEntry entry = jarIn.getNextJarEntry();
List<String> nameList = new ArrayList<String>();
while (null != entry) {
String name = entry.getName();
if (name.startsWith(splashedPackageName) && isClassFile(name)) {
nameList.add(name);
}
entry = jarIn.getNextJarEntry();
}
return nameList;
}
private List<String> readFromDirectory(String path) {
File file = new File(path);
String[] names = file.list();
if (null == names) {
return null;
}
return Arrays.asList(names);
}
private boolean isClassFile(String name) {
return name.endsWith(".class");
}
private boolean isJarFile(String name) {
return name.endsWith(".jar");
}
public Map<String,Method> getMethod(String className) throws Exception{
Map<String,Method> methodMap = new HashMap<String,Method>();
Class<?> cl = Class.forName(className);
Method[] methods = cl.getDeclaredMethods();
for(Method method : methods) {
methodMap.put(method.getName(), method);
}
return methodMap;
}
public static void main(String[] args) throws Exception {
ClasspathPackageScanner scan = new ClasspathPackageScanner("com.techsun.mttrace.servlet");
// List<String> list = scan.getFullyQualifiedClassNameList();
Map<String,Method> map = scan.getMethod("com.techsun.mttrace.servlet.auth.AuthApiServlet");
Iterator<Map.Entry<String, Method>> entries = map.entrySet().iterator();
while(entries.hasNext()) {
Map.Entry<String, Method> entry = entries.next();
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
Method method = entry.getValue();
MethodJson mj = method.getAnnotation(MethodJson.class);
System.out.println(mj.methodRep()+","+mj.methodReq());
}
}
}
接口定义参数对象类:
public class JsonResult {
private String url;
private String reqJson;
private String repJson;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getReqJson() {
return reqJson;
}
public void setReqJson(String reqJson) {
this.reqJson = reqJson;
}
public String getRepJson() {
return repJson;
}
public void setRepJson(String repJson) {
this.repJson = repJson;
}
@Override
public String toString() {
return "JsonResult [url=" + url + ", reqJson=" + reqJson + ", repJson=" + repJson + "]";
}
}
测试用例实体类如下:
实体类用例一:
public class UploadOutRridBoxInfoReq extends BaseAuthReq{
private String outBill;
private String collectWhCode;
private String boxNum;
private List<RfidBoxInfo> boxList;
}
public class BaseAuthReq {
private String userAccount;
private String token;
}
public class RfidBoxInfo {
private String boxTid;
private String boxEpc;
private String barcode;
private String collectTime;
private String deviceId;
private String spec;
}
实体类用例二:
public class GetAbnormalTongji4Rep extends PageListRep<GetAbnormalTongji4Model>{
}
public class PageListRep<T> extends BaseJsonRep {
private List<T> list;
private Page page;
}
public class BaseJsonRep {
private StatusCode statusCode;
private String message = "";
protected String rep;
}
public class GetAbnormalTongji4Model {
private String bill;
private String teamNo;
private String time;
private int planNum;
private int countBack4;
private int countFront4;
}