• 动态代理实现机制深层次分析与动态字节码生成总结


    在上一次【https://www.cnblogs.com/webor2006/p/9847915.html】已经通过源码的方式将动态代理字节码文件生成出来了,如下:

    下面来分析一下该字节码的内容,双击打开IDE就可以反编译其字节码的内容,如下:

    package com.sun.proxy;
    
    import com.jvm.bytecode.Subject;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class $Proxy0 extends Proxy implements Subject {
        private static Method m1;
        private static Method m2;
        private static Method m3;
        private static Method m0;
    
        public $Proxy0(InvocationHandler var1) throws  {
            super(var1);
        }
    
        public final boolean equals(Object var1) throws  {
            try {
                return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        public final String toString() throws  {
            try {
                return (String)super.h.invoke(this, m2, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final void request() throws  {
            try {
                super.h.invoke(this, m3, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final int hashCode() throws  {
            try {
                return (Integer)super.h.invoke(this, m0, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m3 = Class.forName("com.jvm.bytecode.Subject").getMethod("request");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }

    其中可以看到有个传一个InvocationHandler对像的构造函数,其实在生成该代理对象的源码中有用到它,咱们来看一下:

    然后再通过反射来生成该代理类对象的具体实例:

    其中再回到代理类的反编译代码中可以发现总共有四个方法,如下:

    另外还有四个静态的成员变量及对这四个变量进行初始化的代码,如下:

    然后从初始化中就可以看到通过反射,m0、m1、m2这三个方法都是来自于java.lang.Object类,而m3是来自于我们自己编写的Subject.request()方法。下面再来具体来看一下方法的实现:

    很显然就是我们调用时的第三个参数,如下:

    所以对于方法的执行最终都会转向我们自定义的DynamicSubject类中的invoke()方法,如下:

    这其实就是动态代理的一个本质,明白了这个方法的整个调用流程,那其它代理类的方式就基本类似,如下:

    其中还可以得出一个结论就是:如果调用了动态代理中的Object.equals()、Object.toString()、Object.hashCode()三个方法都会转向到我们在DynamicSubject定义的invoke()方法,但是如果调用了Object除这三个方法之外的则不会受动态代理的影响,其实这个在Proxy类的说明中就有解释,如下:

    接下来咱们整体来挼一下整个动态调用的流程,彻底地来理解动态代理实现原理,从我们的调用开始:

    此时就会转到我们分析代理对象的request()当中了,如下:

    然后super.h是提我们传递的实现了InvocationHandler对象,也就是它:

    然后执行该对象的invoke()方法,如下:

    所以此时的调用流程就转到了DynamicSubject.invoke()方法了,如下:

    接着来调用真正代理对象的方法了,如下:

    所以其结果输出为:

    然后再往下:

    自此,对于动态代理从字节码角度就可以掌握得更加踏实啦~~

  • 相关阅读:
    Linux内核初探 之 进程(三) —— 进程调度算法
    Android中的路径记录 | RobinBlog
    子域名劫持
    zookeeper 实战
    [iOS 开发] WebViewJavascriptBridge 从原理到实战 · Shannon's Blog
    Swift Property
    工厂方法模式
    jquery插件封装
    其他事件
    吴裕雄--天生自然诗经学习笔记 :夸父逐日
  • 原文地址:https://www.cnblogs.com/webor2006/p/9875053.html
Copyright © 2020-2023  润新知