天天看點

java反射掃描實體類字段類型描述

問題:

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