项目概况

Javassist

2026-01-293 min read后端技术
字节码增强

!!!info Javassist Javassist是一个开源的分析、编辑和创建Java字节码的类库,可以直接编辑和生成Java生成的字节码。 相对于bcel, asm等这些工具,开发者不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。javassist简单易用, 快速。 !!!

一、核心工具类

核心类解释
ClassPooljavassist的类池,使用ClassPool 类可以跟踪和控制所操作的类,它的工作方式与 JVM 类装载器非常相似
CtClassCtClass提供了类的操作,如在类中动态添加新字段、方法和构造函数、以及改变类、父类和接口的方法。
CtField类的属性,通过它可以给类创建新的属性,还可以修改已有的属性的类型,访问修饰符等
CtMethod类中的方法,通过它可以给类创建新的方法,还可以修改返回类型,访问修饰符等, 甚至还可以修改方法体内容代码
CtConstructor与CtMethod类似

二、API

2.1 ClassPool

java
1 // 类库, jvm中所加载的class 2 ClassPool pool = ClassPool.getDefault(); 3 // 加载一个已知的类, 注:参数必须为全量类名 4 CtClass ctClass = pool.get("com.itheima.Student"); 5 // 创建一个新的类, 类名必须为全量类名 6 CtClass tClass = pool.makeClass("com.itheima.Calculator");

2.2 CtField

java
1 // 获取已知类的属性 2 CtField ctField = ctClass.getDeclaredField("name"); 3 // 构建新的类的成员变量 4 CtField ctFieldNew = new CtField(CtClass.intType,"age",ctClass); 5 // 设置类的访问修饰符为public 6 ctFieldNew.setModifiers(Modifier.PUBLIC); 7 // 将属性添加到类中 8 ctClass.addField(ctFieldNew);

2.3 CtMethod

java
1 // 获取已有方法 2 //创建新的方法, 参数1:方法的返回类型,参数2:名称,参数3:方法的参数,参数4:方法所属的类 3 CtMethod ctMethod = new CtMethod(CtClass.intType, "calc", new CtClass[] 4{CtClass.intType,CtClass.intType}, tClass); 5 // 设置方法的访问修饰 6 ctMethod.setModifiers(Modifier.PUBLIC); 7 // 将新建的方法添加到类中 8 ctClass.addMethod(ctMethod); 9 // 方法体内容代码 $1代表第一个参数,$2代表第二个参数 10 ctMethod.setBody("return $1 + $2;"); 11 12 CtMethod ctMethod = ctClass.getDeclaredMethod("sayHello");

2.4 CtConstructor

java
1 // 获取已有的构造方法, 参数为构建方法的参数类型数组 2 CtConstructor ctConstructor = ctClass.getDeclaredConstructor(new CtClass[]{}); 3 // 创建新的构造方法 4 CtConstructor ctConstructor = new CtConstructor(new CtClass[]{CtClass.intType},ctClass); ctConstructor.setModifiers(Modifier.PUBLIC); 5 ctConstructor.setBody("this.age = $1;"); 6 ctClass.addConstructor(ctConstructor); 7 // 也可直接创建 8 ctConstructor = CtNewConstructor.make("public Student(int age){this.age=age;}", ctClass);

三、示例

java
1public class User { 2 3 private String name; 4 5 public User() { 6 } 7 8 public User(String name) { 9 this.name = name; 10 } 11 12 public String sayJavassist() { 13 return "Hello Javassist"; 14 } 15 16}

3.1 修改方法

java
1 // 类库池, jvm中所加载的class 2 ClassPool pool = ClassPool.getDefault(); 3// 获取指定的Student类 4 CtClass ctClass = pool.get("com.example.test.User"); 5// 获取sayHello方法 6 CtMethod ctMethod = ctClass.getDeclaredMethod("sayJavassist"); 7// 在方法的代码后追加 一段代码 8 ctMethod.insertAfter("System.out.println("I'm Javassist.");"); 9// 使用当前的ClassLoader加载被修改后的类 10 Class<?> newClass = ctClass.toClass(); 11User user = (User) newClass.newInstance(); 12System.out.println(user.sayJavassist());

3.2 动态添加方法

java
1 // 类库池, jvm中所加载的class 2 ClassPool pool = ClassPool.getDefault(); 3// 获取指定的Student类 4 CtClass ctClass = pool.get("com.example.test.User"); 5// 增加方法 6 CtMethod ctMethod = new CtMethod(CtClass.intType, "getAgeSum", 7 new CtClass[]{CtClass.intType, CtClass.intType}, ctClass); 8// 设置方法的访问修饰 9 ctMethod.setModifiers(Modifier.PUBLIC); 10// 设置方法体代码 11 ctMethod.setBody("return $1 + $2;"); 12// 添加新建的方法到原有的类中 13 ctClass.addMethod(ctMethod); 14// 加载修改后的类 15 ctClass.toClass(); 16// 创建对象 17 User stu = new User(); 18// 获取calc方法 19 Method dMethod = User.class.getDeclaredMethod("getAgeSum", new Class[] 20 {int.class, int.class}); 21// 反射调用 方法 22 Object result = dMethod.invoke(stu, 10, 20); 23System.out.println(result);

3.3 动态创建类

java
1 // 类库池, jvm中所加载的class 2 ClassPool pool = ClassPool.getDefault(); 3// 创建一个学校类 4 CtClass schoolClass = pool.makeClass("com.example.test.School"); 5// 设置为公有类 6 schoolClass.setModifiers(Modifier.PUBLIC); 7// 获取String类型 8 CtClass stringClass = pool.get("java.lang.String"); 9// 获取list类型 10 CtClass listClass = pool.get("java.util.List"); 11// 获取学生的类型 12 CtClass userClass = pool.get("com.example.test.User"); 13// 给学校添加一个校名属性 14 CtField nameField = new CtField(stringClass, "schoolName", schoolClass); 15nameField.setModifiers(Modifier.PUBLIC); 16schoolClass.addField(nameField); 17// 给学校添加一个学生集合 18 CtField studentList = new CtField(listClass, "users", schoolClass); 19studentList.setModifiers(Modifier.PUBLIC); 20schoolClass.addField(studentList); 21// 给学校一个空构造 22 CtConstructor ctConstructor = CtNewConstructor.make("public School() " + 23 "{this.schoolName="湖畔小学";this.users = new java.util.ArrayList();}", schoolClass); 24schoolClass.addConstructor(ctConstructor); 25 26// 给学校一个addUser的方法 27 CtMethod m = new CtMethod(CtClass.voidType, "addUser", new CtClass[]{userClass}, schoolClass); 28m.setModifiers(Modifier.PUBLIC); 29// 添加学生对象到students属性中, $1代表参数1 30 m.setBody("this.users.add($1);"); 31schoolClass.addMethod(m); 32 33// 给学校添加一个介绍的方法 34 CtMethod introduce = new CtMethod(CtClass.voidType, "introduce", new CtClass[]{}, schoolClass); 35introduce.setBody("System.out.println("The School name is " + this.schoolName);"); 36introduce.insertAfter("System.out.println(this.users);"); 37schoolClass.addMethod(introduce); 38 39// 加载修改后的学校 40 Class<?> schoolLoadClass = schoolClass.toClass(); 41// 构建一个学校(空构造) 42 Object school = schoolLoadClass.newInstance(); 43// 获取添加用户方法 44 Method addUserMethod = schoolLoadClass.getDeclaredMethod("addUser", userClass.toClass()); 45addUserMethod.invoke(school, new User("小明")); 46addUserMethod.invoke(school, new User("小张")); 47// 获取介绍方法,把刚才的信息给打印处理 48 Method introduceMethod = school.getClass().getDeclaredMethod("introduce"); 49introduceMethod.invoke(school);
text
1The School name is 湖畔小学 2[User{name='小明'}, User{name='小张'}]