1.1. Javassist介绍
Javassist是一个开源的分析、编辑和创建Java的类库。它是的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。
1.2. ClassPool
1. pool获取系统路径以及JVM的同路径下的class
ClassPool pool = ClassPool.getDefault();
2. 新建一个空的pool,没有任何路径
ClassPool pool = new ClassPool();
3. 新建一个只有系统路径资源的pool
ClassPool pool = new ClassPool(true);
相当于
ClassPool pool = new ClassPool();pool.appendSystemPath(); //添加系统路径
4. 用另一个ClassPool建立父子关系
ClassPool parent = ClassPool.getDefault();ClassPool child = new ClassPool(parent);
默认情况和classloader的双亲委托机制相同,会先从父类查找
子类的池可以设置childFirstLookup 为true优先从子类查找
child.childFirstLookup = true; //此时优先子类查找
1.3. 给classpool增加路径的方法
1. 加入文件资源
pool.insertClassPath("文件路径")
5. 加入url资源
ClassPathpool = new URLClassPath("www.javassist.org", 80, "/java/", "org.javassist.");
pool.insertClassPath(cp);
6. 加入数组资源
byte[] b = a byte array;
String name = class name;
pool.insertClassPath(new ByteArrayClassPath(name, b));
7. 加入类资源
pool.insertClassPath(new ClassClassPath(XXX.Class)));
8. 导入包
pool.importPackage("java.awt");
1.4. Ctclass
1. 获取对象
1. 直接从池中获取(推荐)
CtClass cc=pool.get(“类名”);
2. //可以从输入流中加载class(实际就是重新定义了一个类)
InputStream ins = an input stream for reading a class file;
CtClass cc = pool.makeClass(ins);
注意:使用输入流的形式在加载有继承,组合,依赖,聚合等关系的类时,需要将用到的类都加入才能进行修改等操作,否则会出异常。建议结构简单的类来使用此方法。
3. 定义一个class
CtClass cc = pool.makeClass("类名");
2. 常用方法
1. writeFile
writeFile()
写入内存,如何对CtClass进行了修改,内存中就是修改后的类
writeFile("文件路径")
此时会将修改后的字节码输出到相应的文件路径上,此方法可以用来查看我们的修改是否和预期相同
2. toClass
toClass()
Class clazz = cc.toClass();
此时由当前线程的classloader加载这个class
Class clazz = cc.toClass(loader, domain);
指定classloader来加载
writeFile(), toClass(), or toBytecode()后CtClass对象就被冻结,不允许进行修改需要调用defrost();才能继续修改
3. setName
改变类名
cc.setName("Pair");
当CtClass对象被冻结后可以通过pool来更改类名
CtClass cc2 = pool.getAndRename("Point", "Pair");
CtClass从pool对象移除
cc.detach();
4. 删除现有元素
removeField,removeMethod,removeConstructor
5. 添加元素
addField,addMethod,addConstructor
2.1. javassist.Loader
Loader cl = new Loader(pool);
2.2. CtConstructor
1可以通过Ctclass对象的getConstructor的方法获取已经存在的构造函数
2直接新建CtConstructor ,需要设置访问权限
constructor.setModifiers(Modifier.PUBLIC); //设置为public
3CtNewConstructor的make方法新建或者defaultConstructor方法
CtNewConstructor.make(parameters, exceptions, declaring);
CtNewConstructor.defaultConstructor(arg0)
4调用CtNewConstructor的copy方法复制
setBody()设置方法体
2.3. CtMethod
1可以通过Ctclass对象的getDeclaredMethod的方法获取已经存在的方法
2直接新建CtMethod
3通过CtNewMethod的make方法来新建
4调用CtNewMethod的copy方法复制
注意:
通过CtMethod method = new CtMethod(返回类型, 名字,参数类型, CtClass对象);cc.addMethod(method );
此时加入的方法没有方法体,默认是个抽象方法,CtClass对象也变为抽象类
在添加方法之后设置方法体后,需要修改CtClass对象的权限,去掉abstractmethod .setBody("XXX");cc.setModifiers(cc.getModifiers() & ~Modifier.ABSTRACT);
2.4. CtField
1可以通过Ctclass对象的getField的方法获取已经存在的成员变量
2直接新建CtField
3通过CtField的make方法来新建
在CtClass的addField的方法中可以对新建的成员变量初始化
成员函数的修改
增添代码段
insertBefore等
都是插入单行语句或者是一个代码块{}
{ before-statements; 原来的内容 after-statements; }
调用方法参数
2.4.1.1. $0, $1, $2, ... $0表示this,$1表示第一个参数$2表示第二个参数
2.4.1.2. $args 表示 所有参数是一个object[] ,$args[0]表示$1
2.4.1.3. $$ 表示所有参数等同与$1 $2 $3……
2.4.1.4. $r 表示方法的返回类型