zoukankan      html  css  js  c++  java
  • JDK动态代理源码分析

    准备如下:

    public interface Subject {
        void request(String req);
    }
    
    //需要被代理的类
    public class RealSubject implements Subject {
        @Override
        public void request(String req) {
            System.out.println(req);
        }
    }
    
    //代理方法执行的类
    
    public class MyInvocationHander implements InvocationHandler {
    
        private Subject subject;
    
        public MyInvocationHander(Subject subject) {
            this.subject = subject;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before ....");
            method.invoke(subject,args); //被代理的方法
            System.out.println("after ....");
            return null;
        }
    }
    //测试类
    public class Demo {
    
        public static void main(String[] args) {
          
            Subject sub=new RealSubject();
            MyInvocationHander h = new MyInvocationHander(sub);
            Subject proxySub =(Subject) Proxy.newProxyInstance(sub.getClass().getClassLoader(), sub.getClass().getInterfaces(), h);
            proxySub.request("haha");
    
        }
    }

     //使用idea打断点

     //看源码切记别扣细节,Proxy.newProxyInstance知道是通过字节码对象反射创建的代理对象,所以目标很明确,需要找到对应的字节码对象

     进入该方法:getProxyClass0

     进入该方法(proxyClassCache.get(loader, interfaces): 这里是重点,如果扣细节,很难找到,使用目标定位法(返回值),直接找到return值的方法

     点get方法后,有很多实现类,该supplier的实现类和方法如下:

     在get方法找到返回V的地方(目标搜索法,也叫返回值法,这个是看源码的技巧):

     进入valueFactory.apply(key, parameter)方法:

     

     字节码是二进制文件,所以ProxyGenerator.generateProxyClass该方法是生成字节码二进制数组;

    点击进入了:

     这个类根据字节码的结构进行生成对应的字节码文件的

     这里的saveGeneratedFiles是一个boolen类型的系统常量,见明之意就是保存生成的字节码文件,默认为false;

     因此,我们设置System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");这个属性就可以生成动态代理的字节码文件了;

    进入:

     

     可见,Object对象的hashcode,equals 和 toString方法都会被代理;

    //设置系统属性,生成字节码看看:

     执行后,可以看到:

     idea打开,会进行反编译,如下:

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    package com.sun.proxy;
    
    import com.yang.jvm.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})).booleanValue();
            } 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(String var1) throws  {
            try {
                super.h.invoke(this, m3, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        public final int hashCode() throws  {
            try {
                return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
                m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
                m3 = Class.forName("com.yang.jvm.Subject").getMethod("request", new Class[]{Class.forName("java.lang.String")});
                m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }

    //根据字节码可以知道,我们调用代理类的方法时,本质调用的是InvocationHandler的invoke方法,而InvocationHandler是我们自己实现的

     最终在被代理对象的方法执行前后进行了相应的业务代码;

    问题1: jdk动态代理为何只能对接口起作用,不能是类?

    原因:

    public final class $Proxy0 extends Proxy implements Subject {}
    动态代理生成的类会继承Proxy,而java是单继承的,所以不能再继承了,所以只能通过实现来拓展

    问题2: InvocationHandler是什么时候传入动态代理类的呢?
    Proxy的源码,有个构造器:

     源码生成字节码后,通过反射创建对象时,传入了h:

     以上就是jdk动态代理原理了! spring为何使用了jdk动态代理还要使用cglib动态代理呢?原因跟第一个问题答案一样

  • 相关阅读:
    js--事件--事件代理
    bind call apply 的区别和使用
    自己手动用原生实现bind/call/apply
    HLSL GLSL CG着色语言比较
    AABB和OBB包围盒简介
    BSTR LPSTR LPWSTR CString VARIANT COleVariant variant t CC
    UE4蓝图简介
    3D MAX脚本原理
    VC中GetLastError 获取错误信息的使用
    最强偏振3D播放器TriDef 3D安装+全格式播放配置
  • 原文地址:https://www.cnblogs.com/yangxiaohui227/p/11757820.html
Copyright © 2011-2022 走看看