前言
使用javassist做动态代理,请移步23种设计模式之代理模式-Proxy。javassist是一个编辑创建java字节码的类库,由一位日本人创建的。前段时间有个项目是基于JDK6开发的,不是maven项目,要手动导入依赖的jar包,这个jar包的class文件都是jdk6的,如果换成一个jdk8的class文件,那么项目就会因为class文件的jdk版本不兼容启动失败。所以使用javassist修改jdk6的class文件。
1、Javassist
功能
主要的类:
- ClassPool // 操作前都要创建一个 class池
- CtClass // 具体的操作class的对象
- CtMethod // class文件中的 某个方法对象
- CtField // class文件中的 某个方法字段
主要的方法
- CtClass.addMethod
- CtClass.removeMethod
- CtClass.removeField
- CtClass.writeFile // 写入文件,
- CtClass.addField
- CtMethod.insertBefore
- CtMethod.insertAfter
- CtMethod.insertAt
- CtMethod.setBody
增加成员变量
pom.xml要先导入依赖
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
</dependency>
对上面的SpecialPricePolicyVO.class文件,增加成员变量isOperationFeeCalculated 和get/set方法,代码如下
public class Client {
public static void main(String[] args) throws Exception{
String pathName = "D:\\jacob\\ccs\\ccs_policy_hessian";
// 类的包路径
String className = "com.midea.ccs.base.rpcvo.SpecialPricePolicyVO";
ClassPool cPool = ClassPool.getDefault();
cPool.insertClassPath(pathName);
CtClass cClass = cPool.get(className);
// 增加成员变量
cClass.addField(CtField.make("private String isOperationFeeCalculated;",cClass));
// 增加get方法
CtMethod getOperation = CtMethod.make("public String getIsOperationFeeCalculated(){" +
"return isOperationFeeCalculated;}",cClass);
cClass.addMethod(getOperation);
// 增加set方法
CtMethod setOperation = CtMethod.make("public void setIsOperationFeeCalculated(String isOperationFeeCalculated){}", cClass);
setOperation.setBody("this.isOperationFeeCalculated = isOperationFeeCalculated;");
cClass.addMethod(setOperation);
cClass.writeFile(pathName);
}
}
执行main方法,成功后使用idea打开SpecialPricePolicyVO.class文件,发现增加了字段isOperationFeeCalculated
修改方法
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.NotFoundException;
public class UpdateMethod {
private static String pathName = "D:\\Java\\xxxxx\\test\\bin";
private static String className = "com.lucumt.Test1";
public static void main(String[] args) {
updateMethod();
}
public static void updateMethod(){
try {
ClassPool cPool = new ClassPool(true);
//设置class文件的位置
cPool.insertClassPath(pathName);
// 导入需要引入的 包
cPool.importPackage("com.gdzy.JZFW.service");
cPool.importPackage("java.text");
cPool.importPackage("com.gdzy.JZFW.pojo");
cPool.importPackage("com.gdzy.JZFW.util");
cPool.importPackage("java.net");
cPool.importPackage("java.util");
cPool.importPackage("javax.servlet");
cPool.importPackage("com.sun.syndication");
//获取该class对象
CtClass cClass = cPool.get(className);
//获取到对应的方法
CtMethod cMethod = cClass.getDeclaredMethod("addNumber");
// 整个方法的内容修改
cMethod.setBody("{"long z1 = System.currentTimeMillis();\n"+
" boolean sendOld = false;\n"+
" java.util.Map/*<String, Object>*/ mapparam = new java.util.HashMap();\n" +
" mapparam.put(\"typenameEqual\", \"old_sendEQIM_used\");\n" +
" java.util.List/*<com.gdzy.JZFW.pojo.Useruse>*/ listOldused = this.useruseService.selectList(mapparam);\n"+
" if (listOldused.size() > 0 && ((com.gdzy.JZFW.pojo.Useruse)listOldused.get(0)).getParametervalues().equals(\"1\")) {\n" +
" sendOld = true;\n" +
" }\n"+
" boolean sendNew = false;\n" +
" mapparam.clear();\n" +
" mapparam.put(\"typenameEqual\", \"new_sendEQIM_used\");\n" +
" java.util.List/*<com.gdzy.JZFW.pojo.Useruse>*/ listNewused = this.useruseService.selectList(mapparam);\n" +
" if (listNewused.size() > 0 && ((com.gdzy.JZFW.pojo.Useruse)listNewused.get(0)).getParametervalues().equals(\"1\")) {\n" +
" sendNew = true;\n" +
" }\n"+}");
// 写入class文件
cClass.writeFile(pathName);
} catch (NotFoundException e) {
e.printStackTrace();
} catch (CannotCompileException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
修改方法中的具体行
public static void main(String[] args) throws Exception {
ClassPool classPool = ClassPool.getDefault();
// 必须将class文件放在这个工程编译后的class文件中,路径也对应起来
CtClass ctClass = classPool.get("com.ambitionstone.esa2000.pki.AZTPkiServlet");
//设置方法需要的参数,一定要能匹配起来,而且必须引入这些参数类的包
CtClass[] param = new CtClass[4] ;
param[0] = classPool.get("javax.servlet.http.HttpServletRequest") ;
param[1] = classPool.get("javax.servlet.http.HttpServletResponse") ;
param[2] = classPool.get("int") ;
param[3] = classPool.get("java.lang.String") ;
// 找到需要修改的行所在的方法
CtMethod method = ctClass.getDeclaredMethod("doOnlineValidate", param);
// 在这个方法的182行添加关闭文件流的方法
method.insertAt(182, "fin.close();");
// 将文件写到指定的目录,
ctClass.writeFile("D:\\test") ;
}
注意:
-
在使用<>这样的泛型定义 标识时 要使用 /* */ 将其包括起来
List<String> 要写成 List/*<String>*/
-
引入包
ClassPool cPool = new ClassPool(true); //设置class文件的位置 cPool.insertClassPath(pathName); //如果该文件引入了其它类,需要利用类似如下方式声明 cPool.importPackage("java.util.List");