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>
Javassist指引(二)--ClassPool的更多相关文章
- Javassist指引(一)
目录 原文链接 1. 读写字节码 1.1概述 Javassist是一个Java字节码类库.Java的字节码是包含Java类与接口,并按照一定的顺序存在class文件中. Javassist.CtCla ...
- 字节码编程,Javassist篇二《定义属性以及创建方法时多种入参和出参类型的使用》
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 在上一篇 Helloworld 中,我们初步尝试使用了 Javassist字节编程的 ...
- tornado用户指引(二)------------tornado协程实现原理和使用(一)
摘要:Tornado建议使用协程来实现异步调用.协程使用python的yield关键字来继续或者暂停执行,而不用编写大量的callback函数来实现.(在linux基于epoll的异步调用中,我们需要 ...
- 字节码类库之Javassist
Javassist优势 – 比反射开销小,性能高.–javassist性能高于反射,低于ASM运行时操作字节码可以让我们实现如下功能:– 动态生成 新的类– 动态改变某个类的结构 ( 添加 / 删除 ...
- 跟我一起造轮子 手写springmvc
原创地址:https://www.cnblogs.com/xrog/p/9820168.html 作为java程序员,项目中使用到的主流框架多多少少和spring有关联,在面试的过程难免会问一些spr ...
- 通过微信服务号推送Zabbix告警
近期看到一篇通过微信实现Zabbix告警的文章,但实践时发现,无法成功发送消息. 分析原因,应该是微信公众平台加强了登录验证,在登录时会需要管理员进行扫描二维码操作才能成功登陆后台: 而之前文章中的A ...
- Caused by: java.lang.ClassNotFoundException: javassist.ClassPool
1.错误原因 usage: java org.apache.catalina.startup.Catalina [ -config {pathname} ] [ -nonaming ] { -help ...
- [转载]Javassist 使用指南(二)
======================= 本文转载自简书,感谢原作者!. 原链接如下:https://www.jianshu.com/p/b9b3ff0e1bf8 =============== ...
- Unity AssetBundles and Resources指引 (二) Resources文件夹
本文内容主要翻译自下面这篇文章 https://unity3d.com/cn/learn/tutorials/topics/best-practices/guide-assetbundles-and- ...
随机推荐
- placeholder改变输入框字体颜色
::-webkit-input-placeholder { color: #888;}:-moz-placeholder { color: #888;}::-moz-placeholder{col ...
- CSS基础教程:群组化选择器
常常我们的CSS 样式中会有好几个地方需要使用到相同的设定时,一个一个分开写会是一件满累人的工作,重覆性太高且显得冗长,更不好管理....在CSS 语法的基本设定中,就可以把这几个相同设定的选择器合并 ...
- 常用命令4-文件搜索命令 2- whereis
whereis比较局限,只能查看系统命令.
- 基于spring-boot的测试桩设计--几种常见的controller
第一种:通过@RequestBody,直接将请求体映射到对象 //@RequestBody @RequestMapping(value = "addUser", method = ...
- Kubernetes 调度器实现初探
Kubernetes 调度器 Kubernetes 是一个基于容器的分布式调度器,实现了自己的调度模块.在Kubernetes集群中,调度器作为一个独立模块通过pod运行.从几个方面介绍Kuberne ...
- 【POJ 3261】Milk Patterns
[链接]h在这里写链接 [题意] 给你一个长度为n的序列. 问你能不能在其中找到一个最长的子串. 这个子串至少出现了k次. [题解] 长度越长,就越不可能出现k次 后缀数组+二分. N最大为2 ...
- node.js的path模块
path模块的各种API path.join([...paths]) 参数:paths <string> ,paths参数是字符串,这些字符串按路径片段顺序排列,(A sequence o ...
- 【JZOJ4805】【NOIP2016提高A组模拟9.28】跟踪
题目描述 输入 输出 样例输入 4 2 1 3 1 2 2 3 3 4 样例输出 2 数据范围 解法 预处理出两个陌生人走到各个点的距离. 从石神处开始dfs,判断走到每一个点是否会被抓: 如果会,则 ...
- SPARK Day04
广播变量和累加器 广播变量 广播变量理解图 广播变量使用 val conf = new SparkConf() conf.setMaster("local").setAppName ...
- MSSQL → 04:表的创建与维护
一.创建表 1.1.使用SQL Server Management Studio创建表 ①.打开刚刚建立的CollegeSystemDB数据库,找到表(数据库->CollegeSystemDB- ...