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动态代理呢?原因跟第一个问题答案一样

 

posted @ 2019-10-29 11:39  yangxiaohui227  阅读(134)  评论(0编辑  收藏  举报