锁定老帖子 主题:字节码工具asm使用的一个例子
精华帖 (1) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2012-05-03
最后修改:2012-05-04
ASM 是一个 Java 字节码操控框架。它能够以二进制形式修改已有类或者动态生成类。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
初识字节码,现学现卖。 示例中演示的功能:
jdk自带的代理(InvocationHandler)的不足:
ps:附件中内置了asm4.0的核心类。不用下载jar包可直接运行。 下面是重写父类方法的代码: @Override public void visitEnd() { // 如果originalClass定义了私有成员变量,那么直接在visitMethod中复制originalClass的<init>会报错。 // ALOAD 0 // INVOKESPECIAL cc/RoleService.<init>()V // RETURN // // 调用originalClassName的<init>方法,否则class不能实例化 MethodVisitor mvInit = classWriter.visitMethod(ACC_PUBLIC, INIT, "()V", null, null); mvInit.visitVarInsn(ALOAD, 0); mvInit.visitMethodInsn(INVOKESPECIAL, toAsmCls(originalClassName), INIT, "()V"); mvInit.visitInsn(RETURN); mvInit.visitMaxs(0, 0); mvInit.visitEnd(); // 获取所有方法,并重写(main方法 和 Object的方法除外) Method[] methods = originalClass.getMethods(); for(Method m : methods) { if(!Aops.needOverride(m)) { continue; } Type mt = Type.getType(m); LOG.debug(mt); StringBuilder methodInfo = new StringBuilder(originalClassName); methodInfo.append(".").append(m.getName()); methodInfo.append("|"); Class<?>[] paramTypes = m.getParameterTypes(); for(Class<?> t : paramTypes) { methodInfo.append(t.getName()).append(","); } if(paramTypes.length > 0) { methodInfo.deleteCharAt(methodInfo.length() - 1); } // 方法是被哪个类定义的 String declaringCls = toAsmCls(m.getDeclaringClass().getName()); // 方法 description MethodVisitor mWriter = classWriter.visitMethod(ACC_PUBLIC, m.getName(), mt.toString(), null, null); // insert code here (before) doBefore(mWriter,methodInfo.toString()); int i = 0; // 如果不是静态方法 load this对象 if(!Modifier.isStatic(m.getModifiers())) { mWriter.visitVarInsn(ALOAD, i++); } StringBuilder sb = new StringBuilder(m.getName()); // load 出方法的所有参数 for(Class<?> tCls : m.getParameterTypes()) { Type t = Type.getType(tCls); sb.append(loadCode(t)).append(","); mWriter.visitVarInsn(loadCode(t), i++); if(t.getSort() == Type.LONG || t.getSort() == Type.DOUBLE) { i++; } } LOG.debug(sb.toString()); // super.xxx(); mWriter.visitMethodInsn(INVOKESPECIAL,declaringCls,m.getName(),mt.toString()); // 处理返回值类型 Type rt = Type.getReturnType(m); // 没有返回值 if(rt.toString().equals("V")) { doAfter(mWriter,methodInfo.toString()); mWriter.visitInsn(RETURN); } // 把return xxx() 转变成 : Object o = xxx(); return o; else { int storeCode = storeCode(rt); int loadCode = loadCode(rt); int returnCode = rtCode(rt); mWriter.visitVarInsn(storeCode, i); doAfter(mWriter,methodInfo.toString()); mWriter.visitVarInsn(loadCode, i); mWriter.visitInsn(returnCode); } // 已设置了自动计算,但还是要调用一下,不然会报错 mWriter.visitMaxs(i, ++i); mWriter.visitEnd(); } cv.visitEnd(); } 增加基于javassist 的实现做对比(代码比较长,新增了附件): 更详尽的代码可以到https://github.com/dijingran/dxx-cc/tree/master/cc-tx/src/org/cc/tx/aop/javassit查看。
附上一个两者详细对比的链接:http://blog.sina.com.cn/s/blog_7c7348af0100re3u.html 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2012-05-04
目前我使用asm用来做sql查询后的组装结果集,实现orm的操作
|
|
返回顶楼 | |
发表时间:2012-05-04
很好,顺便问一个小问题
public String doLogin(String user_name,String user_pwd,int type) { ...... } 用反射可以得到doLogin方法的参数类型,参数个数 但是,我想动态的得到 user_name , user_pwd , type 这三个字符串, 就像下面那样 第一个参数类型:java.lang.String,参数字符串:user_name 第二个参数类型:java.lang.String,参数字符串:user_pwd 第三个参数类型:java.lang.String,参数字符串:type 求楼主赐教, 求方法,求思路 谢谢。 |
|
返回顶楼 | |
发表时间:2012-05-04
string2020 写道 很好,顺便问一个小问题
public String doLogin(String user_name,String user_pwd,int type) { ...... } 用反射可以得到doLogin方法的参数类型,参数个数 但是,我想动态的得到 user_name , user_pwd , type 这三个字符串, 就像下面那样 第一个参数类型:java.lang.String,参数字符串:user_name 第二个参数类型:java.lang.String,参数字符串:user_pwd 第三个参数类型:java.lang.String,参数字符串:type 求楼主赐教, 求方法,求思路 谢谢。 附件里恰好有类似的方法,Classes.java这个类里面有获取参数名的方法,也是基于asm的。 |
|
返回顶楼 | |
发表时间:2012-05-04
string2020 写道 很好,顺便问一个小问题
public String doLogin(String user_name,String user_pwd,int type) { ...... } 用反射可以得到doLogin方法的参数类型,参数个数 但是,我想动态的得到 user_name , user_pwd , type 这三个字符串, 就像下面那样 第一个参数类型:java.lang.String,参数字符串:user_name 第二个参数类型:java.lang.String,参数字符串:user_pwd 第三个参数类型:java.lang.String,参数字符串:type 求楼主赐教, 求方法,求思路 谢谢。 也通过反射不就可以了么? |
|
返回顶楼 | |
发表时间:2012-05-04
ak478288 写道 string2020 写道 很好,顺便问一个小问题
public String doLogin(String user_name,String user_pwd,int type) { ...... } 用反射可以得到doLogin方法的参数类型,参数个数 但是,我想动态的得到 user_name , user_pwd , type 这三个字符串, 就像下面那样 第一个参数类型:java.lang.String,参数字符串:user_name 第二个参数类型:java.lang.String,参数字符串:user_pwd 第三个参数类型:java.lang.String,参数字符串:type 求楼主赐教, 求方法,求思路 谢谢。 也通过反射不就可以了么? 反射貌似不行吧 |
|
返回顶楼 | |
发表时间:2012-05-04
ak478288 写道 目前我使用asm用来做sql查询后的组装结果集,实现orm的操作
不是通过反射来做吗?不知道asm在这个场景下能做什么 |
|
返回顶楼 | |
发表时间:2012-05-04
ak478288 写道 string2020 写道 很好,顺便问一个小问题
public String doLogin(String user_name,String user_pwd,int type) { ...... } 用反射可以得到doLogin方法的参数类型,参数个数 但是,我想动态的得到 user_name , user_pwd , type 这三个字符串, 就像下面那样 第一个参数类型:java.lang.String,参数字符串:user_name 第二个参数类型:java.lang.String,参数字符串:user_pwd 第三个参数类型:java.lang.String,参数字符串:type 求楼主赐教, 求方法,求思路 谢谢。 也通过反射不就可以了么? 这个真不行。 另外:楼主提供的Classes类,里面的public static String[] getMethodParamNames(final Method m) 这个方法可行。 谢谢。。 |
|
返回顶楼 | |
发表时间:2012-05-04
string2020 写道 ak478288 写道 string2020 写道 很好,顺便问一个小问题
public String doLogin(String user_name,String user_pwd,int type) { ...... } 用反射可以得到doLogin方法的参数类型,参数个数 但是,我想动态的得到 user_name , user_pwd , type 这三个字符串, 就像下面那样 第一个参数类型:java.lang.String,参数字符串:user_name 第二个参数类型:java.lang.String,参数字符串:user_pwd 第三个参数类型:java.lang.String,参数字符串:type 求楼主赐教, 求方法,求思路 谢谢。 也通过反射不就可以了么? 这个真不行。 另外:楼主提供的Classes类,里面的public static String[] getMethodParamNames(final Method m) 这个方法可行。 谢谢。。 看了一下,确实不行。谢谢提醒 |
|
返回顶楼 | |
发表时间:2012-05-04
mazzystar 写道 ak478288 写道 目前我使用asm用来做sql查询后的组装结果集,实现orm的操作
不是通过反射来做吗?不知道asm在这个场景下能做什么 首先通过反射获得类信息和属性字段映射信息,然后通过asm创建一个RowMapper的子类,并缓存这个类生成的对象,这样每次进行ResultSet的处理,全都是和普通编程一样的处理,都不用通过反射赋值。 我目前的另外一个准开源项目就是这样做的, |
|
返回顶楼 | |