`
ezerg
  • 浏览: 269347 次
  • 性别: Icon_minigender_1
  • 来自: 石家庄
社区版块
存档分类
最新评论

使用 ASM 动态生成字节码代替 JAVA 反射提高性能

阅读更多

    有时候为了程序的灵活性,我们都会用到 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();
	}
}


2
0
分享到:
评论
1 楼 sjk_009 2011-08-18  
方法的参数如果是动态的怎么弄?

相关推荐

Global site tag (gtag.js) - Google Analytics