Java字节码Javassist之ClassPool(二)


  ClassPool对象是CtClass对象的容器,一旦创建了CtClass对象,它就会被永久地记录在ClassPool中。这是因为编译器稍后在编译引用由该CtClass表示的类的源代码时可能需要访问CtClass对象。
  例如,假设一个新的方法getter()被添加到表示Point类的CtClass对象中。稍后,程序尝试编译源代码包括Point中的getter()方法调用,并使用编译后的代码作为方法体,该方法体将被添加到另一个类Line。如果CtClass对象表示点丢失,编译器不能编译方法调用的getter()。请注意,最初的类定义不包括getter()。因此,为了正确地编译这样的方法调用,ClassPool必须包含程序执行期间所有的CtClass实例。

避免内存溢出

如果CtClass对象的数量变得惊人的大,ClassPool的这种规范可能会导致巨大的内存消耗。为了避免这个问题,您可以显式地从ClassPool中删除一个不必要的CtClass对象。如果你在一个CtClass对象上调用detach(),那么该CtClass对象将从ClassPool中移除。例如:

CtClass cc = ... ;
cc.writeFile();
cc.detach();

在detach()被调用之后,你不能调用CtClass对象上的任何方法。但是,您可以在ClassPool上调用get()来创建一个表示相同类的CtClass的新实例。如果调用get(), ClassPool将再次读取类文件并新建一个CtClass对象并返回。
  另一个想法是用一个新的ClassPool替换一个旧的ClassPool。如果一个旧的ClassPool被垃圾回收,该ClassPool中包含的CtClass对象也被垃圾收集。要创建一个新的ClassPool实例,请执行以下代码片段:

ClassPool cp = new ClassPool(true);

这将创建一个ClassPool对象,其行为与ClassPool. getdefault()返回的默认ClassPool相同。ClassPool.getDefault()是为了方便而提供的单例工厂方法。
  注意,new ClassPool(true)是一个方便的构造函数,它构造一个ClassPool对象并将系统搜索路径附加到它。调用该构造函数等价于以下代码:

ClassPool cp = new ClassPool();
cp.appendSystemPath();  // or append another path by appendClassPath()

级联的ClassPool

  如果一个程序运行在web应用服务器上,创建多个ClassPool实例可能是必要的;应该为每个类装入器(即容器)创建一个ClassPool实例。程序应该不调用getDefault()而是调用ClassPool的构造函数来创建ClassPool对象。
  多个ClassPool对象可以像java.lang.ClassLoader那样级联。例如:

ClassPool parent = ClassPool.getDefault();
ClassPool child = new ClassPool(parent);
child.insertClassPath("./classes");

  如果调用child.get(),子ClassPool首先委托给父ClassPool。如果父ClassPool未能找到类文件,则子ClassPool将尝试在/classes目录下查找类文件。
如果child.childFirstLookup为true,则子ClsssPool在委托给父ClassPool之前尝试查找类文件。例如:

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.

改变类名来定义新类

  新类可以定义为现有类的副本。下面的程序可以做到这一点:

ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("Point");
cc.setName("Pair");

这个程序首先获取类Point的CtClass对象。然后调用setName()为该CtClass对象赋予一个新名称Pair。在此调用之后,由该CtClass对象表示的类定义中出现的所有类名都从Point更改为Pair。类定义的其他部分没有改变。
  注意CtClass中的setName()改变了ClassPool对象中的一条记录。从实现的角度来看,ClassPool对象是CtClass对象的哈希表。setName()更改哈希表中与CtClass对象相关的键。键从原来的类名更改为新的类名。
  因此,如果稍后在ClassPool对象上再次调用get(“Point”),则它永远不会返回变量cc引用的CtClass对象。ClassPool对象再次读取类文件Point.class,并为类Point构造一个新的CtClass对象。这是因为与名称Point关联的CtClass对象不再存在。请看以下内容:

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.

cc1和cc2引用了与cc相同的CtClass实例,而cc3没有。注意,在执行cc. setname (“Pair”)之后,cc和cc1引用的CtClass对象表示Pair类。
  ClassPool对象用于维护类和CtClass对象之间的一对一映射。Javassist从不允许两个不同的CtClass对象表示同一个类,除非创建了两个独立的ClassPool。这是一致的程序转换的一个重要特性。
  要创建由ClassPool. getdefault()返回的ClassPool默认实例的另一个副本,请执行以下代码片段(上面已经显示了该代码):

ClassPool cp = new ClassPool(true);

如果您有两个ClassPool对象,那么您可以从每个ClassPool中获得表示相同类文件的不同CtClass对象。您可以对这些CtClass对象进行不同的修改,以生成类的不同版本。

重命名冻结类

  一旦CtClass对象被writeFile()或toBytecode()转换为类文件,Javassist将拒绝对该CtClass对象的进一步修改。因此,在表示Point类的CtClass对象被转换为类文件后,您不能将Pair类定义为Point的副本,因为在Point上执行setName()会被拒绝。下面的代码片段是错误的:

ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("Point");
cc.writeFile();
cc.setName("Pair");    // wrong since writeFile() has been called.

为了避免这个限制,你应该在ClassPool中调用getAndRename()。例如,

ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("Point");
cc.writeFile();
CtClass cc2 = pool.getAndRename("Point", "Pair");

如果调用getAndRename(), ClassPool首先读取Point.class以创建一个表示Point类的新CtClass对象。但是,在将该CtClass对象记录到哈希表之前,它将该CtClass对象从“点到对”重命名。因此,在表示Point类的CtClass对象上调用writeFile()或toBytecode()后,可以执行getAndRename()。


[资料英文来源]  http://www.javassist.org/tutorial/tutorial.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值