我是靠谱客的博主 大力毛豆,最近开发中收集的这篇文章主要介绍高效反射ReflectASM原理学习ReflectAS,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

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的源码了解到了它的执行过程:

  1. 反射Demo.class获取公共非静态方法
  2. 用ASM技术动态生成新的继承于MethodAccess的类DemoMethodAccess
  3. 在私有变量内记录方法名称参数等信息
  4. 在类的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所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(38)

评论列表共有 0 条评论

立即
投稿
返回
顶部