一.介绍
Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。在dubbo生成代理对象默认就是使用的javassist。
二.使用
1.几个重要的类:
ClassPool : 一个基于HashMap
实现的CtClass
对象容器,其中键是类名称,值是表示该类的CtClass
对象。默认的ClassPool
使用与底层JVM
相同的类路径,因此在某些情况下,可能需要向ClassPool
添加类路径或类字节。
CtClass :表示一个类, 从ClassPool中获取
CtConstructor: 表示构造方法
CtMethod:表示方法
CtField:表示字段,属性
2.动态生成一个类
maven坐标:
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.22.0-GA</version>
</dependency>
ClassPool pool =ClassPool.getDefault();// 获取classpool
// 创建类
CtClass ctClass = pool.makeClass("com.laoxu.desgin.javassist.ProxyTest");
ctClass.setInterfaces(new CtClass[]{pool.makeInterface("java.lang.Cloneable")}); // 实现某个接口
//ctClass.setModifiers(); 设置 AccessFlag(public , private 等)
// ctClass.setSuperclass(); 继承某个类
try {
// 创建一个字段 类型 字段名 类
CtField id = new CtField(CtClass.intType, "id", ctClass);
id.setModifiers(AccessFlag.PRIVATE);
// 将字段添加到类中
ctClass.addField(id);
// 创建构造方法
CtConstructor constructor = CtNewConstructor.make("public ProxyTest (int id){this.id= id;}", ctClass);
ctClass.addConstructor(constructor);
// 创建method
CtMethod method = CtNewMethod.make("public void print(String str){System.out.println(str);}", ctClass);
ctClass.addMethod(method);
// 写出, 生成实体文件
ctClass.writeFile();
// 生成class对象
//ctClass.toClass();
} catch (CannotCompileException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NotFoundException e) {
e.printStackTrace();
}
执行后,在idea里面就可以看到.class文件。
反编译后:
package com.laoxu.desgin.javassist;
public class ProxyTest implements Cloneable {
public int id;
public ProxyTest(int var1) {
this.id = var1;
}
public void print(String var1) {
System.out.println(var1);
}
}
3.动态修改方法体
现在有一个PrintTest类,其中有个print方法,我们想要在print方法前添加某段代码,在print方法后添加某段代码。说白了就是实现AOP的效果。
public class PrintTest {
public void print(String str){
System.out.println(str);
}
}
修改:
ClassPool pool =ClassPool.getDefault();
try {
// 获取ctclass 对象
CtClass ctClass = pool.getCtClass("com.laoxu.desgin.javassist.PrintTest");
// 获取方法
CtMethod printMethod = ctClass.getDeclaredMethod("print");
// 添加前置
printMethod.insertBefore("System.out.println(\"before\");");
//添加后置
printMethod.insertAfter("System.out.println(\"after\");");
ctClass.writeFile();
// 生成class对象
Class clazz = ctClass.toClass();
Object instance = clazz.newInstance();
Method method = clazz.getMethod("print", String.class);
// 反射执行
method.invoke(instance,"2019-08");
} catch (NotFoundException e) {
e.printStackTrace();
} catch (CannotCompileException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
后面我们对生成这个代理对象进行反射执行修改后的方法,得到结果:
before
2019-08
after
我们在看下生成的那个代理对象
package com.laoxu.desgin.javassist;
public class PrintTest {
public PrintTest() {
}
public void print(String str) {
System.out.println("before");
System.out.println(str);
Object var3 = null;
System.out.println("after");
}
}