問題:
由于項目技術無法使用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;
}