Java 反射 抽取類的方法信息
目前參與的項目是用 Spring MVC + MyBatis 實現的,項目部署就是一個war包。公司從外面請了個顧問,建議將公司網絡分為A、B兩個區,B區的安全級別高些,可以訪問數據庫,A區的安全級別低些, 不能訪問數據庫,直接面向互聯網,應用需要訪問外部互聯網服務時 或 外部用戶請求應用時都必須在 A 區完成,A區通過定制的網關訪問 B 區的應用。這個建議是強制執行,所以就需要拆分項目了。
考慮到開發的方便性,A區與B區之間就必須工作在類似Hessian之類的遠程調用上,而不能直接在http層上,要不然裝包拆包都累死人了。
項目目前的代碼層次是 Rest 風格的 Controller + Service + MyBatis 的 Mapper。Controller 里大量使用servlet的API,所以不能把controller層抽取出來作為遠程調用的接口。Mapper本身只是一個接口,service層與 mapper層之間沒法再拆,只能在controller與service之間拆。項目里沒有專門為每個 service 組件定義一個相應的接口,需要根據已有的service組件抽取出對應的接口。
由于組件太多,只能寫工具類抽取。
工具類的目標:
- 抽取所有組件的公開方法作為接口的方法,保留方法定義的類型信息和參數名等信息。
- 生成接口所依賴的導入并拷貝所有依賴的導入類。
- 生成對應的Hessian配置。 </ol>
- 把指定類型的公開方法抽取出來,生成一個名為("I" + 類名)的接口的源碼,包路徑相同,并拷貝所依賴的項目源碼。
- @author coderbee
/
public class InterfaceExtractor {
/**
- 要跳過的方法名
*/
private final List<String> FILTER_METHOD = Arrays.asList("wait", "equals",
/**"toString", "hashCode", "getClass", "notify", "notifyAll", "main");
- 不需要導入的基本類型
*/
private static final List<String> FILTER_TYPE = Arrays.asList("boolean",
private String srcDir; // 源碼目錄 private String dstDir; // 生成代碼的存放目錄 // 保存要導入的類型 private List<String> imports = new LinkedList<String>(); // 生成的接口源碼 private StringBuilder bodyBuilder = new StringBuilder(1024); /**"char", "byte", "int", "short", "double", "float", "long", "void");
- @param srcDir
- 源碼目錄
- @param dstDir
- 生成代碼的存放目錄 */ public InterfaceExtractor(String srcDir, String dstDir) { this.srcDir = srcDir; this.dstDir = dstDir; } /**
- 抽取指定類型的接口
- @param claz
- 表示類的 Class 實例
*/
public void extractInterface(@SuppressWarnings("rawtypes") Class claz) {
String fullName = getNormalName(claz);
String path = getPkgPath(fullName);
String pkg = "package " + path + ";\n\n";
bodyBuilder.append("public interface I").append(claz.getSimpleName())
.append(" {\n");
Method[] methods = claz.getMethods();
for (Method method : methods) {
if (!FILTER_METHOD.contains(method.getName())) {
addMethod(bodyBuilder, method);
bodyBuilder.append('\n');
}
}
bodyBuilder.setCharAt(bodyBuilder.length() - 1, '}');
writeFileD(dstDir + getIName(fullName) + ".java", pkg + getImports()
- "\n" + bodyBuilder); } private void copyImports(String type) { String subPath = File.separatorChar
- type.replace('.', File.separatorChar) + ".java"; File srcFile = new File(srcDir + subPath); if (srcFile.exists()) { File dstFile = new File(dstDir + subPath); copy(srcFile, dstFile); } } private String getImports() { Collections.sort(imports); StringBuilder importBuilder = new StringBuilder(1024); for (String imp : imports) { importBuilder.append("import ").append(imp).append(";\n"); copyImports(imp); } return importBuilder.toString(); } /**
- 添加一個方法的聲明
- @param sb
- @param method */ private void addMethod(StringBuilder sb, Method method) { bodyBuilder.append('\t'); addReturnType(method); bodyBuilder.append(' ').append(method.getName()).append('('); // 添加方法名 addParameter(method); bodyBuilder.append(");\n"); } /**
- 添加方法的參數
- @param sb
- @param method
*/
private void addParameter(Method method) {
Type[] paramTypeList = method.getGenericParameterTypes();
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
} } private String getTypeName(Type type) { if (type instanceof ParameterizedType) {Type paramType = paramTypeList[i]; addTypeParameters(paramType); Parameter parameter = parameters[i]; bodyBuilder.append(' ').append(parameter.getName()); if (i != parameters.length - 1) { bodyBuilder.append(", "); }
} else {// 泛型 return getGenericTypeName((ParameterizedType) type);
} } private String getGenericTypeName(ParameterizedType type) { Type[] actualTypeArguments = type.getActualTypeArguments(); if (actualTypeArguments == null || actualTypeArguments.length == 0) {// 非泛型 return getSimpleTypeName(type.getTypeName());
} else {// 泛型的類型非參數化的 return getSimpleTypeName(type.getTypeName());
} } private String genericRawType(String typeName) { int indexOf = typeName.indexOf('<'); return typeName.substring(0, indexOf); } private String getSimpleTypeName(String typeName) { if (typeName.startsWith("[")) {StringBuilder sb = new StringBuilder(64); String typeName = genericRawType(type.getTypeName()); sb.append(getSimpleTypeName(typeName)); sb.append('<'); for (Type genericType : actualTypeArguments) { sb.append(getTypeName(genericType)).append(", "); } sb.setCharAt(sb.length() - 2, '>'); return sb.deleteCharAt(sb.length() - 1).toString();
} return getSimpleNameByFullName(typeName); } private String getArrayTypeName(String typeName) { // 第二個字符表示數組元素的類型 char ch = typeName.charAt(1); if (ch == 'Z') {// 返回的類型是數組 return getArrayTypeName(typeName);
} else if (ch == 'B') {return "boolean[]";
} else if (ch == 'C') {return "byte[]";
} else if (ch == 'S') {return "char[]";
} else if (ch == 'I') {return "short[]";
} else if (ch == 'F') {return "int[]";
} else if (ch == 'D') {return "float[]";
} else if (ch == 'J') {return "double[]";
} else if (ch == 'L') {return "long[]";
} else {// 對象數組 String tName = typeName.substring(2, typeName.indexOf(';')); return getSimpleNameByFullName(tName) + "[]";
} } /**return "";
- 通過類完整路徑名獲取類的簡單名字,并加入導入列表。
- @param fullName
- 類完整路徑名
- @return 簡單名字
*/
private String getSimpleNameByFullName(String fullName) {
String[] split = fullName.split("\.");
if (split.length == 3 && fullName.startsWith("java.lang.")) {
} else {// java.lang 包下的不需要顯式 import return split[2];
} } /**if (!FILTER_TYPE.contains(fullName) && fullName.length() > 1 && !imports.contains(fullName) && !fullName.contains("[")) { // 過濾數組參數類型(如:int[] ) imports.add(fullName); } return split[split.length - 1];
- 添加方法的返回類型
- @param sb
- @param method
*/
private void addReturnType(Method method) {
if (method.getName().endsWith("getDeuAmoArr")) {
} Type genericReturnType = method.getGenericReturnType(); if (genericReturnType instanceof ParameterizedType) {System.out.println("hold");
} else {// 泛型 String typeName = getGenericTypeName((ParameterizedType) genericReturnType); bodyBuilder.append(typeName);
} } /**// 非泛型 Class<?> returnType = method.getReturnType(); String returnTypeName = getSimpleTypeName(returnType.getName()); bodyBuilder.append(returnTypeName);
- 添加泛型的實際類型
- @param sb
- @param type
- 返回類型或方法參數的類型 */ private void addTypeParameters(Type type) { String typeName = type.getTypeName(); if (type instanceof ParameterizedType) { bodyBuilder.append(getGenericTypeName((ParameterizedType) type)); } else { String[] split = typeName.split(" ", 2); if (split.length > 1) { bodyBuilder.append(split[1]); } else { bodyBuilder.append(getSimpleTypeName(typeName)); } } } /**
- 獲取 包 路徑
- @param fullName
- @return */ private String getPkgPath(String fullName) { int i = fullName.lastIndexOf('.'); return fullName.substring(0, i); } /**
- 獲取代碼所聲明的類的完整路徑名
- @param claz
- @return
*/
private String getNormalName(@SuppressWarnings("rawtypes") Class claz) {
String name = claz.getName();
int i = name.indexOf('$');
if (i != -1) {
} return name; } private static String getIName(String fullName) { String[] split = fullName.split("\."); StringBuilder sb = new StringBuilder(fullName.length()); for (int i = 0; i < split.length - 1; i++) {name = name.substring(0, i);
} sb.append('I').append(split[split.length - 1]); return sb.toString(); } public static void writeFileD(String fullPath, String body) { FileOutputStream fout = null; try {sb.append(split[i]).append(File.separatorChar);
} catch (IOException e) {File file = new File(fullPath); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } fout = new FileOutputStream(file); fout.write(body.getBytes("UTF-8"));
} finally {e.printStackTrace();
} } private void copy(File srcFile, File dstFile) { File dstDir = dstFile.getParentFile(); if (!dstDir.exists()) {closeQuietly(fout);
} FileInputStream inputStream = null; FileOutputStream outputStream = null; try {dstDir.mkdirs();
} catch (FileNotFoundException e) {inputStream = new FileInputStream(srcFile); outputStream = new FileOutputStream(dstFile); inputStream.getChannel().transferTo(0, srcFile.length(), outputStream.getChannel());
} catch (IOException e) {e.printStackTrace();
} finally {e.printStackTrace();
} } public static void closeQuietly(Closeable closeable) { if (closeable != null) {closeQuietly(inputStream); closeQuietly(outputStream);
} } }</pre>try { closeable.close(); } catch (IOException ignored) { }
- 要跳過的方法名
*/
private final List<String> FILTER_METHOD = Arrays.asList("wait", "equals",
要保留方法的參數名信息需要 Java 8 的特性。Java 8 的 javac
增加了一個選項 -parameters
,表示在生成的字節碼文件里保留方法的參數名。
具體源碼
package net.coderbee.demo.util; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; /**
本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!