有时候为了程序的灵活性,我们都会用到 JAVA 的反射机制,但是它的代价就是性能比较差,特别是高并发的情况下。
我们可以通过动态的修改字节码代替反射,以期获得更高的性能。当然它还可以做更多的事情,例如 Spring 的 AOP 实现就使用该技术。
动态修改字节码的框架很多,这里使用 ASM ,因为它简洁、方便而且高效。
假设一个处理 WEB 请求的 Action ,每个 Action 通过 execute() 方法来处理请求,为了使用方便让一个 Action 处理多个请求,一般有两种实现方式:
一、是使用反射
在 WEB 请求可以增加一个参数,告诉 Action 要执行哪个方法:
String cmd = getActionMethodName(method);
try {
Method m = action.getClass().getMethod(cmd, paras);
Object[] objs = new Object[] { form, module };
return m.invoke(action, objs);
} catch (NoSuchMethodException nsme) {// getMethod
log.error("找不到可执行命令{}", method);
throw new NoSuchMethodException("您访问的页面不存在");
} catch (InvocationTargetException ite) {// invoke
log.error("执行 method 命令出错", ite);
throw new Exception(ite.getCause().getMessage());
} catch(Exception e) {// other exception
log.error("执行 doActionMehtod 方法出错", e);
return null;
}
这种方式实现比较方便,但是每次请求都会用到反射。
二、利用 ASM 动态修改字节码
基本的原理是 Action 中除 execute() 以外的每个方法都生成一个子类,子类继承该 Action 并实现 exeucte() 方法,子类只对父类特定方法的封装。
public class ParentAction implements IAction {
public Page execute(Form f, Module m) throws Exception {
return m.defaultPage();
}
public Page doChildMethod(Form f, Module m) throws Exception {
return m.defaultPage();
}
}
可以动态生成子类:
public class ChildAction extends ParentAction {
public Page execute(Form f, Module m) throws Exception {
return doChildMethod();
}
}
也就是说每个执行 ParentAction 中 doChildMethod() 方法的请求都会执行 ChildAction 的 exeucte() 方法,这就避免使用反射机制。
// 以下只列出核心的代码
public class MethodActionEnhancer implements Opcodes {
public Object getMethodAction(String cls, String method) {
// 新类的全称
String newcls = cls + method;
try {
return Class.forName(newcls).newInstance();
} catch (Exception e) {
log.warn("未找到类[{}],自动生成", newcls);
try {
byte[] b = dump(cls, method, newcls);
return lc.loadClass(newcls, b).newInstance();
} catch (Exception ex) {
log.error("类[" + newcls + "]加载出错", ex);
return null;
}
}
}
private byte[] dump(String cls, String method, String newcls)
throws Exception {
String f = "Lcom/ezerg/jwdt/web/Form;";
String m = "Lcom/ezerg/jwdt/web/Module;";
String p = "Lcom/ezerg/jwdt/web/Page;";
// 父类的名称
cls = cls.replace(".", "/");
// 子类的名称
newcls = newcls.replace(".", "/");
// 构建 ASM 工具类对象
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
MethodVisitor mv;
cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, newcls, null, cls, null);
cw.visitSource(newcls + ".java", null);
{ // 构造方法,每个类必须
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);// 方法名称
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLineNumber(7, l0);
mv.visitVarInsn(ALOAD, 0);// 加载 this 对象
mv.visitMethodInsn(INVOKESPECIAL, cls, "<init>", "()V");// 调用父类的 init 方法
mv.visitInsn(RETURN);// 返回
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLocalVariable("this", "L" + newcls + ";", null, l0, l1, 0);// 本地方法变量
mv.visitMaxs(1, 1);// 设置本地堆栈
mv.visitEnd();
}
{ // execute方法,实现子类的相关方法
mv = cw.visitMethod(ACC_PUBLIC, "execute", "(" + f + m + ")" + p, null, new String[] { "java/lang/Exception" });// 方法名称
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLineNumber(10, l0);
mv.visitVarInsn(ALOAD, 0);// 加载 this 对象
mv.visitVarInsn(ALOAD, 1);// 加载 f 参数
mv.visitVarInsn(ALOAD, 2);// 加载 m 参数
mv.visitMethodInsn(INVOKEVIRTUAL, cls, method, "(" + f + m + ")" + p);// 调用父类的方法
mv.visitInsn(ARETURN); // 返回对象的引用
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLocalVariable("this", "L" + newcls + ";", null, l0, l1, 0);// 本地方法变量
mv.visitLocalVariable("f", f, null, l0, l1, 1);// 本地方法变量
mv.visitLocalVariable("m", m, null, l0, l1, 2);// 本地方法变量
mv.visitMaxs(3, 3);// 设置本地堆栈
mv.visitEnd();
}
cw.visitEnd();
return cw.toByteArray();
}
}
分享到:
相关推荐
CGLIB是基于ASM库,通过字节码技术动态生成子类来实现代理。Spring框架默认使用CGLIB作为AOP(面向切面编程)的底层实现。 动态代理的应用场景广泛,包括: - AOP:在不修改源代码的情况下,为方法添加预处理和后...
另外,字节码操作库如ASM、CGLIB和ByteBuddy则可以用于动态生成或修改类的字节码,常用于AOP框架和性能优化。 4. **HTTP通信**: 在Java中,处理HTTP请求和响应最常用的是`java.net.HttpURLConnection`,它是Java...
学习CGLIB,不仅有助于深入理解Spring AOP的工作原理,还能提高我们对Java字节码、反射以及动态代理等高级特性的掌握。通过阅读和分析Spring源码,我们可以看到CGLIB是如何与Spring框架无缝集成的,这将对提升我们的...
CGLib的核心是ASM库,它允许在运行时生成和修改类的字节码。通过生成并写入新的类文件,CGLib可以创建目标类的子类,然后在子类的方法上插入自定义的行为。 3. **Enhancer类**: 在CGLib中,`Enhancer`是主要的...
同时,工具类如CGLIB和ASM也提供了对Java动态代理的支持,它们可以生成字节码来创建代理类,适用于没有接口的情况。 总的来说,动态代理是Java中一个强大的工具,它让我们可以在运行时灵活地扩展和增强已有对象的...
- **性能比较**:根据不同的场景和需求,这些工具在字节码生成效率上的表现有所不同,一般认为ASM的性能最高,其次是Javassist,然后是CGLib和JDK自带工具。 ##### 线程模型与网络编程 1. **线程**:线程是操作...