ClassPool详解

转自:http://www.tuicool.com/articles/nQNn6z

1.简介


ClassPool对象是一个CtClass对象的容器。一个CtClass对象被构建后,它被记录在ClassPool中。这是因为当编译的原文件关联到CtClass表示的类, 编译器要访问CtClass对象。

例如,假定一个新方法getter()要加入到CtClass对象表示的的Point类。程序试图编译Point中的方法getter()的源代码,用编译过的代码做为方法内容,将它加到另一个类Line中。如果CtClass对象表示的Point丢失了,编译器将不能编译getter()方法。注册初始的类不包含getter()方法。因此,为了正确的编译一个方法,ClassPool必须拥有程序运行时所有的CtClass实例。

2. 免内存溢出


ClassPool的特点决定了当 CtClass对象数量很多时,它所占的内存会非常大。为了避免这种情况发生,你可以明确的移除一个ClassPool中的不需要的CtClass对象。如果用CtClass的detach()方法,CtClass对象将从ClassPool中移除。例如:

<span style="font-size:18px;">cc.detach();</span>

执行deatach()后,将不能执行CtClass对象的任何方法。但是,可以执行ClassPool的get()方法得到表示同一个类的CtClass实例。执行get()后,ClassPool再次读取类文件,重新建立CtClass对象。

另一个办法是用新的ClassPool替换老的。如果老的 ClassPool当做垃圾被回收了,它里面的CtClass对象也会被回收,建立新的ClassPool实例,执行下面的代码片段:


<span style="font-size:18px;">ClassPool cp = new ClassPool(true);
// if needed, append an extra search path by appendClassPath()</span>

通过ClassPool.getDefault()构建默认行为的ClassPool对象。注意ClassPool.getDefault()是为了方便提供的单子工厂方法。它保持着单独的对象并重用它。getDefault()返回的ClassPool对象没有特别的作用。getDefault()是一个方便的方法。

注意 new ClassPool(true)是个方便的构造器,它构建一个ClassPool对象并加入系统搜索路径。执行它等同于下面的代码:


<span style="font-size:18px;">ClassPool cp = new ClassPool();
cp.appendSystemPath(); // or append another path by appendClassPath()</span>

3. 级联的ClassPools


如果程序运行在web服务器中,可能需要建立多个ClassPool;要为每个类载入器建一个ClassPool。程序将不用getDefault(),用ClassPool构造器建一个ClassPool对象。多个CLassPool对象像java.lang.ClassLoader一样级联起来。例如,

<span style="font-size:18px;">ClassPool parent = ClassPool.getDefault();
ClassPool child = new ClassPool(parent);
child.insertClassPath("./classes");</span>

如果执行child.get(),孩子ClassPool首先去查找父ClassPool。如果说父查找失败,孩子再去./classes目录下查找类。
如果child.childFirstLookup为true,先在孩子中查找,再到父中查。例如:


<span style="font-size:18px;">ClassPool parent = ClassPool.getDefault();
ClassPool child = new ClassPool(parent);
child.appendSystemPath();// the same class path as the default one.
child.childFirstLookup = true; // changes the behavior of the child.</span>


4. 改变类名,定义一个新类


一个新类可以被定义为已经存在类的拷贝。看下面的程序:

<span style="font-size:18px;">ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("Point");
cc.setName("Pair");</span>

这个程序首先得到Point作为CtClass对象,然后用setName()为CtClass设置了新的名字Pair,之后CtClass对象的类名被改为Pari,类定义的其他部分没有变。 注意 CtClass的setName()改变了ClassPool中的一个记录。从实现的角度 ,setName()改变的是ClassPool 的hash表中的CtClass对象关联的key。Key从初始的类名改到了新类名。
因此,如果get(“Point”) 再次被执行后,将不能返回cc引用的CtClass对象。ClassPool再次读取Point.class类文件,为Point构建新的CtClass对象。这是因为Point命名的CtClass对象已经不存在了。看下面例子:

<span style="font-size:18px;">ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("Point");
CtClass cc1 = pool.get("Point"); // cc1 is identical to cc.
cc.setName("Pair");
CtClass cc2 = pool.get("Pair"); // cc2 is identical to cc.
CtClass cc3 = pool.get("Point"); // cc3 is not identical to cc.</span>


Cc1和cc2引用同一个CtClass实例cc,而cc3不是。注意,cc.setName(“Pair”)执行后,CtClass对象Pair 被cc和cc1引用。
ClassPool对象用来维持类和CtClass对象之间一对一的关系映射。Javassist不允许用两个有区别的CtClass对象表示同一个类,除非建两个单独CLassPool。这是对程序转换前后有意义的功能。

建立另一个ClassPool默认实例的拷贝,用ClassPool.getDefault()得到,执行下面代码:

ClassPool cp = new ClassPool(true);如果有两个ClassPool对象,可以分别从ClassPool中得到表示同一个类文件的有区别的CtClass对象。可以编辑不同的CtClass对象用来生成不同的类的版本。 改变冻结类名,定义一个新类
当CtClass对象通过writeFile()或toBytecode()转换为类文件,Javassist将拒绝CtClass对象的修改。因此CtClass表示的Point类是被转换到类文件中了,执行setName()不能定义Pair类作为Point的拷贝,它将被拒绝。下面的代码是错误的:


<span style="font-size:18px;">ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("Point");
cc.writeFile();cc.setName("Pair"); // wrong since writeFile() has been called.</span>

为了避免这个限制,可以执行ClassPool的getAndRename()方法,例如:

<span style="font-size:18px;">ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("Point");
cc.writeFile();
CtClass cc2 = pool.getAndRename("Point", "Pair"); </span>

如果执行getAndRename(),ClassPool首先读取Point.class建立一个新的CtClass。但是,它重命名CtClass从Point改为Pair以前,要先在CtClass的Hash表中作记录。因此,writeFile()或toBytecode()执行后可以执行getAndRename()。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值