从本系列前面的文章中,您了解到反射的性能比直接访问要慢许多倍,并了解了用 Javassist 和 Apache Byte Code Engineering Library (BCEL)进行classworking。Java 顾问 Dennis Sosnoski 通过演示如何使用运行时 classworking,来用全速前进的生成代码 取代反射代码,从而结束他的 Java 编程的动态性系列。
既然您已经看到了如何使用 Javassist 和 BCEL 框架来进行 classworking ,我将展示 一个实际的 classworking 应用程序。这个应用程序用运行时生成的、并立即装载到 JVM 的 类来取代反射。在综合讨论的过程中,我将引用本系列的前两篇文章,以及对 Javassist 和 BCEL 的讨论,这样本文就成为了对这个很长的系列文章的一个很好的总结。
反射的性能
在 第 2 部分,我展示了无论是对于字段访问还是方法调用,反射都比直接代码慢很多倍 。这种延缓对于许多应用程序来说不算是问题,但是总是会遇到性能非常关键的情况。在这 种情况下,反射可能成为真正的瓶颈。但是,用静态编译的代码取代反射可能会非常混乱, 并且在有些情况下(如在这种框架中:反射访问的类或者项目是在运行时提供的,而不是作 为这一编译过程的一部分提供的),如果不重新构建整个应用程序就根本不可能取代。
Classworking 使我们有机会将静态编译的代码的性能与反射的灵活性结合起来。这里的 基本方法是,在运行时,以一种可以被一般性代码使用的方式,构建一个自定义的类,其中 将包装对目标类的访问(以前是通过反射达到的)。将这个自定义类装载到 JVM 中后,就可 以全速运行了。
设置阶段
清单 1 给出了应用程序的起点。这里定义了一个简单的 bean 类 HolderBean 和一个访 问类 ReflectAccess 。访问类有一个命令行参数,该参数必须是一个值为 int 的 bean 类 属性的名字( value1 或者 value2 )。它增加指定属性的值,然后在退出前打印出这两个 属性值。
清单 1. 反射一个 bean
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 class ReflectAccess
{
public void run(String[] args) throws Exception {
if (args.length == 1 && args[0].length() > 0) {
// create property name
char lead = args[0].charAt(0);
String pname = Character.toUpperCase(lead) +
args[0].substring(1);
// look up the get and set methods
Method gmeth = HolderBean.class.getDeclaredMethod
("get" + pname, new Class[0]);
Method smeth = HolderBean.class.getDeclaredMethod
("set" + pname, new Class[] { int.class });
// increment value using reflection
HolderBean bean = new HolderBean();
Object start = gmeth.invoke(bean, null);
int incr = ((Integer)start).intValue() + 1;
smeth.invoke(bean, new Object[] {new Integer(incr)});
// print the ending values
System.out.println("Result values " +
bean.getValue1() + ", " + bean.getValue2());
} else {
System.out.println("Usage: ReflectAccess value1|value2");
}
}
}
下面是运行 ReflectAccess 的两个例子,用来展示结果:
[dennis]$ java -cp . ReflectAccess value1
Result values 1, 0
[dennis]$ java -cp . ReflectAccess value2
Result values 0, 1