概述
ReflectAS
M原理学习
最近学习了静态代理、动态代理等技术,进而知道了ASM jar包,
知道了以此jar包为基础开发的一个高性能的反射包ReflectASM。
想起自己之前写代码的时候使用的都是java自带的反射包对性能有些许浪费,
而这jar包号称几乎直接在代码里调用方法一样的性能,顿时兴起学习之心。
ReflectASM有这么神奇吗?使用了反射还能那么高效?
打开jar包后发现其实就4个类而已,确实非常的小
看源码如果只是反射调用方法其实只用到了
AccessClassLader 和MethodAccess两个类
反射目标
package cn.lsg.codegenerate;
public class Demo {
private String a;
private Integer b;
public String getA() {
return a;
}
public Integer getB() {
return b;
}
public void setA(String a) {
this.a = a;
}
public void setB(Integer b) {
this.b = b;
}
}
main方法
public static void main(String[] args) throws IOException {
Demo d=new Demo();
d.setA("asasa");
d.setB(50);
MethodAccess m = MethodAccess.get(Demo.class);
Object invoke = m.invoke(d, "getA");
System.out.println(invoke);
}
读MethodAccess的源码了解到了它的执行过程:
- 反射Demo.class获取公共非静态方法
- 用ASM技术动态生成新的继承于MethodAccess的类DemoMethodAccess
- 在私有变量内记录方法名称参数等信息
在类的invoke方法内实现调用不同的方法
我想看看 invoke 方法的内容,发现很难实现,起初想使用对象流将class
对象保存下来,但发现class未实现序列化接口,断点调试也无法进入方法。
后来想了个取巧的办法,直接建立一个新的类将MethodAccess内容复制出来,
修改了其中的代码static public MethodAccess get (Class type) { ...... synchronized (loader) { try { accessClass = loader.loadClass(accessClassName); } catch (ClassNotFoundException ignored) { String accessClassNameInternal = accessClassName.replace('.', '/'); String classNameInternal = className.replace('.', '/'); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); MethodVisitor mv; //将继承类的路径换成新的(全部替换) 这里换成cn/lsg/codegenerate/MethodAccess cw.visit(V1_1, ACC_PUBLIC + ACC_SUPER, accessClassNameInternal, null, "cn/lsg/codegenerate/MethodAccess", new String[]{"java/io/Serializable"}); ...... byte[] data = cw.toByteArray(); //添加的代码 FileOutputStream fout; try { fout = new FileOutputStream("C:/Users/Administrator/Desktop/新建文件夹/"+accessClassName+".class"); fout.write(data); fout.close(); } catch (IOException e) { e.printStackTrace(); } accessClass = loader.defineClass(accessClassName, data); } //添加的代码 } try { MethodAccess access = (MethodAccess)accessClass.newInstance(); access.methodNames = methodNames; access.parameterTypes = parameterTypes; access.returnTypes = returnTypes; return access; } catch (Exception ex) { throw new RuntimeException("Error constructing method access class: " + accessClassName, ex); } }
在返回对象前将存储字节码的byte数组输出到了文件里。
用反编译器反编译了生成的class文件 终于知道了实现方式
package cn.lsg.codegenerate;
import java.io.Serializable;
public class DemoMethodAccess extends MethodAccess
implements Serializable
{
public Object invoke(Object paramObject, int paramInt, Object[] paramArrayOfObject)
{
Demo localDemo = (Demo)paramObject;
switch (paramInt)
{
case 0:
localDemo.setA((String)paramArrayOfObject[0]);
return null;
case 1:
localDemo.setB((Integer)paramArrayOfObject[0]);
return null;
case 2:
return localDemo.getA();
case 3:
return localDemo.getB();
}
throw new IllegalArgumentException("Method not found: " + paramInt);
}
}
原来如此,难怪性能能和直接调用媲美 这根本就是直接调用方法
但是在调用方法之前使用了反射、又生成了字节码还进行了新类的加载 这三部一样
是很耗时的。如果将从开始到结束的所有时间算进去 其实它相比其他反射并不怎么占优,但如果只生成一次代理类的话其性能就很高了。所以使用这个jar包要注意缓存
生成的class对象。尽量少重复生成。
最后
以上就是大力毛豆为你收集整理的高效反射ReflectASM原理学习ReflectAS的全部内容,希望文章能够帮你解决高效反射ReflectASM原理学习ReflectAS所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复