Javassist 是什么 ?
javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。
使用Javassist
1.动态生成接口实现类
先来看看我们的接口Person,就是一个简单的接口
package com.yad.javassitdemo.t1;
public interface Person {
void say (String msg );
}
现在我们不使用硬编码而是通过Javassist来生成接口实现类
package com.yad.javassitdemo.t1;
import javassist.*;
import java.lang.reflect.Method;
/*
使用javassist 来动态生成实现接口的Class
*/
public class Test2 {
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
//com.yad.javassitdemo.t1.Man
String clazzName = Person.class.getName()+".Man";
// public class Man {}
CtClass targetClass = pool.makeClass(clazzName);
//获得接口的CtClass
CtClass interf = pool.getCtClass(Person.class.getName());
CtClass[] interfs = new CtClass[]{interf};
// public class Man implements Person{}
targetClass.setInterfaces(interfs);
CtMethod[] personMethods = interf.getDeclaredMethods();
for (CtMethod method : personMethods){
CtMethod m = new CtMethod(method.getReturnType(),method.getName(),method.getParameterTypes(),targetClass);
//$0 = this $1 , $2 表示方法参数
m.setBody("{System.out.println($1);}");
targetClass.addMethod(m);
}
//生成Class文件
targetClass.writeFile("C:\\Users\\yad\\Code\\OpenSource\\yorm\\src\\main\\resources");
//生成Class载入内存
Class target = targetClass.toClass();
//实例化
Object instance = target.newInstance();
//调用方法
Method method = instance.getClass().getMethod("say",String.class);
method.invoke(instance,"hello world");
}
}
来看看生成的Class文件
package com.yad.javassitdemo.t1;
public class Man implements Person {
public void say(String var1) {
System.out.println(var1);
}
public Man() {
}
}
2.动态生成抽象类的实现类
先创建一个抽象类
package com.yad.javassitdemo.t1;
public abstract class Computer {
public abstract int sum(int a ,int b);
}
用Javassist来生成抽象类的实现类
package com.yad.javassitdemo.t1;
import javassist.*;
import java.lang.reflect.Field;
/**
* 动态继承抽象类并实现
*/
public class Test3 {
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
//com.yad.javassitdemo.t1.Computer
String abstractName = Computer.class.getName();
//相当于com.yad.javassitdemo.t1.ComputerV1
String instanceName = abstractName +"V1";
//获得Computer 抽象类的 CtClass
CtClass abstractClass = pool.get(abstractName);
//public class ComputerV1 extends Computer{}
CtClass targetClass = pool.makeClass(instanceName,abstractClass);
//这里就只添加 sum 方法进行实现
CtMethod[] methods = abstractClass.getDeclaredMethods();
for (CtMethod m : methods){
if (m.getName().equals("sum")){
CtMethod newMethod = new CtMethod(m.getReturnType(),m.getName(),m.getParameterTypes(),targetClass);
//相当于 return a + b;
newMethod.setBody("{ return $1+$2;}");
targetClass.addMethod(newMethod);
}
}
//添加属性
CtField version = CtField.make("public int version ;",targetClass);
targetClass.addField(version,CtField.Initializer.constant(1)); //设置初始化属性
targetClass.writeFile("C:\\Users\\yad\\Code\\OpenSource\\yorm\\src\\main\\resources");
//测试方法
Class realClass = targetClass.toClass();
Computer instance = (Computer) realClass.newInstance();
int sum = instance.sum(1, 2);
System.out.println(sum);
//测试属性
Field Fversion = instance.getClass().getField("version");
Integer versionValue = (Integer) Fversion.get(instance);
System.out.println(versionValue);
}
}
来看看生成的字节码
package com.yad.javassitdemo.t1;
public class ComputerV1 extends Computer {
public int version = 1;
public int sum(int var1, int var2) {
return var1 + var2;
}
public ComputerV1() {}
}
3.动态的给一个类的方法插入代码
首先我们来看看我们要进行修改的类
package com.yad.javassitdemo.t1;
public class Man implements Person {
public Man(){}
@Override
public void say(String msg) {
//通过Javassist 在执行方法前插入代码
System.out.println(msg);
//通过Javassist 在执行方法后插入代码
}
}
使用Javassit 进行修改
package com.yad.javassitdemo.t1;
import javassist.*;
import java.lang.reflect.Method;
public class Test4 {
public static void update() throws Exception {
ClassPool pool = ClassPool.getDefault();
// 参数不能是 Man.class.getName() 因为这样子ClassLoader会把 Man.class 直接载入内存
CtClass manCtClass = pool.get("com.yad.javassitdemo.t1.Man");
CtMethod method = manCtClass.getDeclaredMethod("say",new CtClass[]{pool.get(String.class.getName())});
//修改方法
method.insertBefore("System.out.println(\" 执行方法之前前 \");");
method.insertAfter("System.out.println(\" 执行方法之后后 \");");
manCtClass.writeFile("C:\\Users\\yad\\Code\\OpenSource\\yorm\\src\\main\\resources");
Class manCLass = manCtClass.toClass();
Object instance = manCLass.newInstance();
Method say = Man.class.getMethod("say", String.class);
say.invoke(instance,"hello world");
}
public static void main(String[] args) {
try {
update();
} catch (Exception e) {
e.printStackTrace();
}
}
}
来看看修改后的Class 文件
package com.yad.javassitdemo.t1;
public class Man implements Person {
public Man() {
}
public void say(String msg) {
System.out.println(" 执行方法之前前 ");
System.out.println(msg);
Object var3 = null;
System.out.println(" 执行方法之后后 ");
}
}
可以看到原来的类被修改了,这个应该就能够实现AOP
上面通过Javassist提供的API我们很方便的进行对Class的动态创建和修改
这样我们可以摆脱重复的硬编码,使用反射和Javassist 灵活的创建修改我们想要的Class