目前参与的项目是用 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配置。
要保留方法的参数名信息需要 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笔记,可以更及时回复你的讨论。