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组件抽取出对应的接口。

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

工具类的目标:

  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) {
               }
          }
     }
}

欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据