反射雖靈活但性能低下(比直接調用慢數十倍)。本文作為「Java 動態編程」系列收官之作,展示如何用 Javassist 與 BCEL 在運行時生成「膠水類」,實現接口 IAccess,將反射調用轉為直接方法調用。實測 512K 循環下,生成代碼比反射快 24 倍;甚至在 2K 循環時,含類生成/加載耗時的總開銷仍優於反射。
定義接口作為靜態編譯期契約:
public interface IAccess {
void setTarget(Object target);
int getValue();
void setValue(int value);
}
目標 Bean:
public class HolderBean {
private int value1, value2;
public int getValue1() { return value1; }
public void setValue1(int v) { value1 = v; }
public int getValue2() { return value2; }
public void setValue2(int v) { value2 = v; }
}
反射版本需查找 getValue1/setValue1 並 Method.invoke();生成版本直接調用 m_target.getValue1()。
關鍵 API:ClassPool、CtClass、CtField、CtMethod、CtConstructor。
protected byte[] createAccess(Class targetClass, Method getter, Method setter, String className) {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass(className);
cc.addInterface(pool.get("IAccess"));
CtClass target = pool.get(targetClass.getName());
// 字段
cc.addField(new CtField(target, "m_target", cc));
// 無參構造器
cc.addConstructor(new CtConstructor(new CtClass[0], cc).setBody("{;}"));
// setTarget
cc.addMethod(new CtMethod(CtClass.voidType, "setTarget",
new CtClass[]{pool.get("java.lang.Object")}, cc)
.setBody("m_target = (" + targetClass.getName() + ")$1;"));
// getValue
cc.addMethod(new CtMethod(CtClass.intType, "getValue", new CtClass[0], cc)
.setBody("return m_target." + getter.getName() + "();"));
// setValue
cc.addMethod(new CtMethod(CtClass.voidType, "setValue",
new CtClass[]{CtClass.intType}, cc)
.setBody("m_target." + setter.getName() + "($1);"));
return cc.toBytecode();
}
操作粒度細至 JVM 指令,代碼量較大但控制力最強。核心類:ClassGen、MethodGen、InstructionList、InstructionFactory、ConstantPoolGen。完整代碼見原文 Listing 5,邏輯與 Javassist 版等價:構造字段、構造器、三個接口方法的字節碼指令序列。
測試環境:Athlon 2200+,Sun JVM 1.4.2,Linux。循環次數 2K ~ 512K。
[dennis]$ java -cp .:bcel.jar BCELCalls 2000
Generate and load time of 409 ms. // 首次含類加載開銷
Reflection took 61 ms. with result 2000
Generated took 2 ms. with result 2000
Generate and load time of 1 ms. // 二次複用已生成類
Reflection took 13 ms. with result 2000
Generated took 2 ms. with result 2000

生成的類需被靜態代碼訪問,採用 SecureClassLoader 子類 DirectLoader:
protected static class DirectLoader extends SecureClassLoader {
protected DirectLoader() { super(TimeCalls.class.getClassLoader()); }
protected Class load(String name, byte[] data) {
return defineClass(name, data, 0, data.length);
}
}
確保生成類在應用類加載器命名空間內可見。
運行時類生成結合了靜態編譯性能與動態靈活性,是解決反射性能瓶頸的「銀彈」。Javassist API 友好、開發效率高;BCEL 控制力極致、適合底層框架開發。下一系列將探討字節碼操作工具生態(如 ASM、Byte Buddy)及實戰案例。