在工作中需要修改一个三方包,并且这个三方包没有开源,通过源码调试发现只需要修改几处代码就能让这个三方包在我们的系统环境下使用起来更简单,这就需要通过直接修改class文件来达到目的,javassist就是这样一个工具
引入jar包
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.29.2-GA</version>
</dependency>
使用
import java.io.IOException;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
/**
* @author dfg
* Created by on 2023-05-24
*/
public class JavassistTest {
public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException {
ClassPool classPool = ClassPool.getDefault();
classPool.insertClassPath("target.jar");
// 引入包
classPool.importPackage("java.util");
classPool.importPackage("java.lang");
// 找到修改的类
CtClass targetClass = classPool.getCtClass("xxx");
// 创建一个属性
CtField targetField = CtField.make("private Map map = new HashMap();", targetClass);
// 通过方法签名设置泛型
targetField.setGenericSignature("Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;");
// 关联属性
targetClass.addField(targetField);
// 创建一个方法 返回值是String类型 入参是一个Integer类型
CtMethod targetMethod = new CtMethod(classPool.get("java.lang.String"), "test", new CtClass[] { classPool.get("java.lang.Integer") }, targetClass);
// 设置方法的修饰符
targetMethod.setModifiers(Modifier.PUBLIC);
// targetMethod.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
// 设置方法体
String methodBody = "{\n"
+ "$0.map.put($1, $1);"
+ "return String.valueOf($1.intValue());"
+ "\n}";
targetMethod.setBody(methodBody);
// 关联方法
targetClass.addMethod(targetMethod);
// 写入
targetClass.writeFile();
}
}
使用方法还是很简单的,注释已经写清楚了,就不再多解释了
关于创建方法体有几个要点要注意一下
- 方法体的关键字
- $0:调用这个方法自身的对象,相当于this
- $1:第一个入参
- $2:第二个入参
- 涉及的类在没有import之前只能使用全限定名
- 通过ClassPool.insertClassPath方法把使用的类先进行导入就可以不使用全限定名,推荐这种方式
- ClassPool.get函数获取CtClass的时候只能使用全限定名
-
方法的入参尽量使用ClassPool.get,因为如果使用ClassPool.makeClass那么对象是一个mock出来的,不具备这个类本身的任何属性与方法
-
在方法体内部不能使用泛型,这个似乎是javassist的一个不足之处
重新打包
- 经过上述步骤拿到了想要的target.class
- 把目标的jar文件的后缀修改成rar或者tar,这样就能使用压缩文件打开了
- 把target.class复制到原先目标的class位置,把老的class文件替换掉
- 再把后缀修改的jar,这样就拿到了一个修改之后的jar,可以快乐的玩耍了