zoukankan      html  css  js  c++  java
  • Javassist指引(二)--ClassPool

    原文链接
    上一章:
    Javassist指引(一)

    2.ClassPool

    ClassPool是一个CtClass的容器。因为编译器随时可能访问一个CtClass类,所以一旦一个CtClass创建,它将永远保存在ClassPool类里面。
    举一个简单的例子,之前我们有一个叫做表示Point类的CtClass实例,我们在里面添加了一个getter()方法。如果这个操作没有被永远地保存,在另外一处使用这个getter方法时又得重新添加。好在不是如此,ClassPool一直保存着这个实例。

    2.1 避免OOM

    因为ClassPool上述特性,随着CtClass越来越多,ClassPool的内存开销会越来越大。为了避免这个问题,我们可以移除一些不必要的CtClass类。我们调用CtClass::detach()方法执行这个操作。

    CtClass ctClass = pool.makeClass(inputStream);
    ctClass.writeFile();
    ctClass.detach();
    

    我们调用了detach后,就不能再调用CtClass的任何方法了。在Javassist3.0中,再次调用该命令会抛出RunTimeException.
    另外一个方法是我们每次使用的时候手工创建一个ClassPool,旧的ClassPool在没有引用后就会被垃圾回收器回收,我们可以模仿ClassPool的getDefault方法。

    ClassPool cp = new ClassPool();
    //append ClassPath
    

    的方法。

    2.2级联ClassPools

    如果这是一个Web程序,那么我们需要多个ClassPool,我们最好为每一个ClassLoader创建一个ClassPool。我们直接调用ClassPool的构造函数而非getDefault方法。

    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.
    

    当我们调用child.get()的时候,程序会首先在parent ClassPool中需找结果,如果没有找到,会尝试在child ClassPool中寻找。
    如果我们想优先在child ClassPool中寻找,我们可以将child.childFirstLookup设置为true

    2.3改变类名以定义新类

    通过改变一个已有的类的名字来定义新类,代码如下:

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

    这段代码首先从ClassPool中取出一个CtClass,然后调用setName的方法。在这次调用后,类名将更改为Pair,但类的其他内容并未发生改变。(举个例子,如果先前有setX的方法,新类也有)。
    需要注意的是,当我们调用setName的时候,将直接在原有类上做修改,不仅如此,先前我们提到ClassPool的实现是以name为key的HashTable,执行完setName之后,hashtable上面的key也会被改变。我们可以通过一段代码验证。

    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.
    

    在Javassist中,一个ClassPool里面的name-object键值对都是1对1的关系。除非我们使用两个不同的ClassPool,Javassist不允许两个不同的CtClass拥有同一个类名。这个是Javassist的一个重要特征。
    如果我们需要同一个类名有多个不同的版本,我们需要再创建一个ClassPool,然后修改新ClassPool中的CtClass。下面是一个例子。

    ClassPool pool = ClassPool.getDefault();
    ClassPool qool = new ClassPool();
    qool.appendSystemPath();
    

    2.4改变冻结类类名

    当一个CtClass执行完writeFile或者toBytecode操作后,Javassist就会拒绝所有对这个CtClass的变更。上上述例子中,如果我们已经把Point冻结,那么我们执行setName将报错。

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

    为了避开这个限制,我们可以调用getAndRename的方法,例如

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

    当我们调用getAndRename的时候,ClassPool会读取Point.class并创建对应的CtClass的类。然后再执行Rename的操作。

    2.5 getAndRename

    事实上,如果我们需要一个类的副本,最快捷的方法便是使用getAndRename,我们点进去getAndRename的实现,会发现这个操作的过程是重新从文件中再加载一遍该类,然后再添加进ClassPool。
    我们通过一个简单的例子来看一下。

          </div>
  • 相关阅读:
    Android开发之LocationManager和定位
    Android开发之SmsManager和SmsMessage
    Android开发之三种动画
    Android开发之ActivityManager获取系统信息
    Android开发之TextView实现跑马灯效果
    Android开发之MD5加密
    linux服务之dns
    java使用Redis(六个类型)
    JedisConnectionException: Failed connecting to host localhost:6379
    下载安装Redis+使用
  • 原文地址:https://www.cnblogs.com/jpfss/p/11060296.html
Copyright © 2011-2022 走看看