ASM classworking 库声称自己又小又快是否名符其实?用 ASM 2.0 测试一下就知道。
简介:在这一期的 Classworking 工具箱中,咨询顾问 Dennis Sosnoski 把 ASM 字节码操作框架与 他以前在 Java 编程动态性系列中讨论过的字节码工程库(Byte Code Engineering Library,BCEL)以 及 Javassist 框架进行比较。ASM 声称自己又小又快 —— 但将它与其他框架进行比较的情况如何样呢 ? Dennis 将采用他在以前系列文章中使用的示例对 ASM 的可用性和性能进行评估。
目前已经开发了若干个处理字节码和类文件的 Java 库,其中包括我在以前的 Java 编程动态性 系列 中介绍的 Javassist 和 BCEL 库。ASM 是这种类型的另一个更新的库。与其他库不同,ASM 被设计和实 现为尽可能小而快。在本月的这一期文章中,我将深入研究 ASM 在这一点上做得到底如何,将它与其他 两个用作本系列中的示例的库进行比较。
在上一期文章中,我演示了如何用运行时字节码生成来代替反射。那次,我使用了 1.4.1 的 JVM 进 行测试,结果发现,生成的代码运行起来可能要比它替换的反射代码更快。除了在 ASM 上采用同样的手 段进行测试之外,在这一期中,我还更新了结果,用 1.5.0 的 JVM 进行测试,看看 1.5.0 中实现的性 能增强是否会改变结果。
代替反射
示例应用程序的目的是用运行时生成的代码代替反射。 在我的 Java 编程动态性 系列中,我已经深入介绍过这个主题。在这一期的文章中,我将对以前的材料 做一个快速的背景总结,然后看看在使用 ASM 代替 Javassist 和 BCEL 框架时,与这两者相比,ASM 的 性能和可用性如何。
设置阶段
反射为在运行时访问对象和元数据提供了非常强大的机制( 正如我在“Java 编程动态性,第 2 部分” 中讨论过的)。使用反射使构建应用程序更加灵 活,可以在运行时用外部信息把各个片断挂接(hook)在一起,形成一个工作配置。但是利用反射来实际 访问对象通常比直接执行相同的操作慢得多。使用基于反射的方法构建应用程序,而后发现需要改进性能 ,这样会带来真正的问题,因为反射支持的灵活性很难以其他方式做到。
Classworking 技术提供 了一种方法。它没有使用反射来访问对象的属性,例如,可以在运行时构建一个类来做同样的事 —— 但这样做会快许多。“Java 编程动态性,第 8 部分”演示了如何用 Javassist 和 BCEL 这两个 classworking 框架来实现这种类型的反射替代。这篇文章采用的基本原则很 简单:首先创建一个接口(该接口定义所需的函数),然后在运行时构建一个类(该类实现前面的接口, 并把函数挂接到目标对象上)。
清单 1 演示了这种方法。在这里,HolderBean 类包含一对属性 ,通过使用反射来调用 get 和 set 方法,可以在运行时访问这一对属性。IAccess 接口抽象化了通过 get 和 set 方法访问 int 值属性的概念,而 AccessValue1 类则特别针对 HolderBean 类的 “value1”属性给出了这个接口的实现。
清单 1. 反射替代接口和实现
public class HolderBean
{
private int m_value1;
private int m_value2;
public int getValue1( {
return m_value1;
}
public void setValue1(int value) {
m_value1 = value;
}
public int getValue2() {
return m_value2;
}
public void setValue2(int value) {
m_value2 = value;
}
}
public interface IAccess
{
public void setTarget(Object target);
public int getValue();
public void setValue(int value);
}
public class AccessValue1 implements IAccess
{
private HolderBean m_target;
public void setTarget(Object target) {
m_target = (HolderBean)target;
}
public int getValue () {
return m_target.getValue1();
}
public void setValue (int value) {
m_target.setValue1(value);
}
}
如果 不得不手工编码诸如清单 1 中的 AccessValue1 那样的每个实现类,那么整个方法可能都不是很有用。 但是 AccessValue1 中的代码非常简单,这使它成为运行时类生成的理想目标。可以使用 AccessValue1 字节码作为模板,以生成特定于具体目标对象类型和 get/set 方法对的类,只要用这些目标替换掉 AccessValue1 中使用的那些目标即可。这是我在以前的文章中使用的方法,也是我在这一期中用在 ASM 上的方法。