Java 反射 抽取类的方法信息

jopen 9年前

目前参与的项目是用 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组件抽取出对应的接口。

由于组件太多,只能写工具类抽取。

工具类的目标:

  1. 抽取所有组件的公开方法作为接口的方法,保留方法定义的类型信息和参数名等信息。
  2. 生成接口所依赖的导入并拷贝所有依赖的导入类。
  3. 生成对应的Hessian配置。

要保留方法的参数名信息需要 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;  /**  * 把指定类型的公开方法抽取出来,生成一个名为("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",                 "char", "byte", "int", "short", "double", "float", "long", "void");       private String srcDir; // 源码目录       private String dstDir; // 生成代码的存放目录       // 保存要导入的类型       private List<String> imports = new LinkedList<String>();       // 生成的接口源码       private StringBuilder bodyBuilder = new StringBuilder(1024);       /**       *        * @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++) {                 Type paramType = paramTypeList[i];                 addTypeParameters(paramType);                 Parameter parameter = parameters[i];                 bodyBuilder.append(' ').append(parameter.getName());                 if (i != parameters.length - 1) {                      bodyBuilder.append(", ");                 }            }       }       private String getTypeName(Type type) {            if (type instanceof ParameterizedType) {                 // 泛型                 return getGenericTypeName((ParameterizedType) type);            } else {                 // 非泛型                 return getSimpleTypeName(type.getTypeName());            }       }       private String getGenericTypeName(ParameterizedType type) {            Type[] actualTypeArguments = type.getActualTypeArguments();            if (actualTypeArguments == null || actualTypeArguments.length == 0) {                 // 泛型的类型非参数化的                 return getSimpleTypeName(type.getTypeName());            } else {                 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();            }       }       private String genericRawType(String typeName) {            int indexOf = typeName.indexOf('<');            return typeName.substring(0, indexOf);       }       private String getSimpleTypeName(String typeName) {            if (typeName.startsWith("[")) {                 // 返回的类型是数组                 return getArrayTypeName(typeName);            }            return getSimpleNameByFullName(typeName);       }       private String getArrayTypeName(String typeName) {            // 第二个字符表示数组元素的类型            char ch = typeName.charAt(1);            if (ch == 'Z') {                 return "boolean[]";            } else if (ch == 'B') {                 return "byte[]";            } else if (ch == 'C') {                 return "char[]";            } else if (ch == 'S') {                 return "short[]";            } else if (ch == 'I') {                 return "int[]";            } else if (ch == 'F') {                 return "float[]";            } else if (ch == 'D') {                 return "double[]";            } else if (ch == 'J') {                 return "long[]";            } else if (ch == 'L') {                 // 对象数组                 String tName = typeName.substring(2, typeName.indexOf(';'));                 return getSimpleNameByFullName(tName) + "[]";            } else {                 return "";            }       }       /**       * 通过类完整路径名获取类的简单名字,并加入导入列表。       *        * @param fullName       *            类完整路径名       * @return 简单名字       */       private String getSimpleNameByFullName(String fullName) {            String[] split = fullName.split("\\.");            if (split.length == 3 && fullName.startsWith("java.lang.")) {                 // java.lang 包下的不需要显式 import                 return split[2];            } else {                 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")) {                 System.out.println("hold");            }            Type genericReturnType = method.getGenericReturnType();            if (genericReturnType instanceof ParameterizedType) {                 // 泛型                 String typeName = getGenericTypeName((ParameterizedType) genericReturnType);                 bodyBuilder.append(typeName);            } else {                 // 非泛型                 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) {                 name = name.substring(0, i);            }            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++) {                 sb.append(split[i]).append(File.separatorChar);            }            sb.append('I').append(split[split.length - 1]);            return sb.toString();       }       public static void writeFileD(String fullPath, String body) {            FileOutputStream fout = null;            try {                 File file = new File(fullPath);                 if (!file.getParentFile().exists()) {                      file.getParentFile().mkdirs();                 }                 fout = new FileOutputStream(file);                 fout.write(body.getBytes("UTF-8"));            } catch (IOException e) {                 e.printStackTrace();            } finally {                 closeQuietly(fout);            }       }       private void copy(File srcFile, File dstFile) {            File dstDir = dstFile.getParentFile();            if (!dstDir.exists()) {                 dstDir.mkdirs();            }            FileInputStream inputStream = null;            FileOutputStream outputStream = null;            try {                 inputStream = new FileInputStream(srcFile);                 outputStream = new FileOutputStream(dstFile);                 inputStream.getChannel().transferTo(0, srcFile.length(),                           outputStream.getChannel());            } catch (FileNotFoundException e) {                 e.printStackTrace();            } catch (IOException e) {                 e.printStackTrace();            } finally {                 closeQuietly(inputStream);                 closeQuietly(outputStream);            }       }       public static void closeQuietly(Closeable closeable) {            if (closeable != null) {                 try {                      closeable.close();                 } catch (IOException ignored) {                 }            }       }  }