• JDK动态代理源码剖析


    关键代码:

    1.Proxy.newInstance:

    private static final Class<?>[] constructorParams = { InvocationHandler.class };
    Class<?> cl = getProxyClass0(loader, intfs);
    final Constructor<?> cons = cl.getConstructor(constructorParams);
    return cons.newInstance(new Object[]{h});
    

    2.Proxy.getProxyClass0:

    // otherwise, it will create the proxy class via the ProxyClassFactory
    return proxyClassCache.get(loader, interfaces);
    

    3.WeakCache.get(由2注释可知,首次是由ProxyClassFactory生成的class对象,proxyClassCache即WeakCache):

    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    

    可见,这里调用ProxyClassFactory的apply方法;

    4.ProxyClassFactory.apply:

    long num = nextUniqueNumber.getAndIncrement();
    String proxyName = proxyPkg + proxyClassNamePrefix + num;
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
    return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
    

    这里先取一个原子整数作为序号,生成代理对象名称,com.sun.proxy.$Proxy0.class,
    然后通过ProxyGenerator.generateProxyClass获得class的字节码,然后调用native方法defineClass0创建class对象。

    由上可知,先在ProxyClassFactory的apply方法中通过ProxyGenerator.generateProxyClass得到字节码,调用defineClass0由字节码得到class对象(是个native方法),然后在Proxy.newInstance里得到这个class对象的构造器,然后反射得到代理对象实例。
    通过ProxyGenerator.generateProxyClass(Openjdk能看到该类的源码)可以生成代理对象的字节码byte[] data,然后将其写到磁盘文件(.class后缀),即可看到代理对象的class文件了:

    FileOutputStream fos = new FileOutPutStream("$Proxy0.class");
    fos.write(data);
    fos.close();
    

    使用jd反编译:

    该class继承了Proxy,实现了被代理对象的接口。构造方法传入InvocationHandler,调用super(invocationHandler);实现的被代理接口的方法里面其实是调用的invocationHandler.invoke方法(Object proxy参数传入的代理对象this)。
    

    使用动态代理:

    1)实现InvocationHandler接口(增强),并持有被代理对象实例,并在它的invoke方法里面写增强逻辑,该方法的参数是代理对象,方法和参数,在invoke合适的位置调用被代理对象的方法(使用反射的方式method.invoke(target, args));
    2)使用Proxy.newInstance创建代理对象,classloader(加载生成的动态代理类字节码为class对象)使用被代理对象的classloader即可(target.getClass.getClassLoader),interfaces使用被代理对象的接口数组(target.getClass.getInterfaces)。
    
    return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
    
  • 相关阅读:
    AngularJS 指令的 Scope (作用域)
    ubuntu16安装使用chrome
    kibana 和ES安装配置常见问题解决
    angular-translate国际化
    安装指定版本的nodejs
    Sass (Syntactically Awesome StyleSheets)
    小程序的网路请求赋值
    c# List<List<object(Id,Name)> 转换成 Dictionary<string, string>
    c# linq List<T> To Excel
    c# 格式化easyui tree
  • 原文地址:https://www.cnblogs.com/kibana/p/10176847.html
Copyright © 2020-2023  润新知