JDK动态代理实现原理

JDK通过在运行时生成一个代理类实现动态代理。

//使用JDK生成代理对象,要定义的一个接口,代理对象也是此接口的实现类
interface HelloInterface {
    void sayHello();
}

//被代理类实现接口
class HelloImpl implements HelloInterface {
    @Override
    public void sayHello() {
        System.out.println("hello world");
    }
}

//动态代理类,用于生成代理对象
class DynamicProxy implements InvocationHandler {

    //本例中,被代理类是HelloImpl
    Object originalObj;

    //返回HelloImpl的代理对象$Proxy0.class的实例
    Object getProxy(Object originalObj) {
        this.originalObj = originalObj;
        return Proxy.newProxyInstance(originalObj.getClass().getClassLoader(),
                originalObj.getClass().getInterfaces(),
                this);
    }

    //代理对象$Proxy0.class的实例运行sayHello(),会调用此方法,此方法切入了前置、后置代码
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("切入前置代码,开始调用目标方法");
        Object proxyObj = method.invoke(originalObj, args);
        System.out.println("切入后置代码,目标方法已经调用结束");
        return proxyObj;
    }
}

public class J_DynamicProxyTest {

    public static void main(String[] args) {
        //在main方法中加入这句代码,程序运行时会在项目根目录生成一个名为$Proxy0.class的代理类Class文件
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

        HelloInterface helloProxy = (HelloInterface) new DynamicProxy().getProxy(new HelloImpl());
        helloProxy.sayHello();

        System.out.println(helloProxy instanceof HelloImpl); //false,helloProxy不是HelloImpl的实例
        System.out.println(helloProxy.getClass().getName()); //com.jvm.proxy.$Proxy0

    }
}

上面是一个JDK动态代理的简单例子。运行main()后会生成一个$Proxy0.class文件。

JDK并不会加载磁盘上的$Proxy0.class文件,事实上JVM是在内存中生成一个描述代理类的byte[]数组,加载的也是这个byte[]数组。通过System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");把byte[]数组写到磁盘中,只是为了读者更好的理解。

HelloInterface helloProxy = (HelloInterface) new DynamicProxy().getProxy(new HelloImpl());
helloProxy.sayHello();

System.out.println(helloProxy instanceof HelloImpl); //false,helloProxy并不是HelloImpl的实例
System.out.println(helloProxy.getClass().getName()); //com.jvm.proxy.$Proxy0

通过上面4行代码可以知道代理对象helloProxy并不是HelloImpl的实例,而是com.jvm.proxy.$Proxy0的实例。

直接用idea打开com.jvm.proxy.$Proxy0.class,主要源码如下:

//$Proxy0继承Proxy并实现HelloInterface
final class $Proxy0 extends Proxy implements HelloInterface {

    private static Method m3;  //sayHello方法

    static {
        try {
            //m3是HelloInterface的sayHello方法
            m3 = Class.forName("com.jvm.proxy.HelloInterface").getMethod("sayHello");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }    

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final void sayHello() throws  {
        try {
            /*
            h是InvocationHandler的实现类,在本例中是DynamicProxy。
            即helloProxy.sayHello();其实是执行DynamicProxy中的invoke方法
            */
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

}

$Proxy0.class的重点代码是这行  super.h.invoke(this, m3, (Object[])null); 

打开$Proxy0父类Proxy的源码

可以看到h属性是InvocationHandler类型,在本例中就是DynamicProxy实例。

DynamicProxy实例是如何被设置到$Proxy0的h属性中的呢?看DynamicProxy中的getProxy方法

Object getProxy(Object originalObj) {
    this.originalObj = originalObj;
    return Proxy.newProxyInstance(originalObj.getClass().getClassLoader(),
            originalObj.getClass().getInterfaces(),
            this);
}

此方法调用了Proxy.newProxyInstance,并将this(DynamicProxy实例)作为参数传给newProxyInstance()。

看下Proxy.newProxyInstance的源码(仅列出主要部分):

public class Proxy implements java.io.Serializable {

    protected InvocationHandler h;

    protected Proxy(InvocationHandler h) {
        this.h = h;
    }

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {

        final Class<?>[] intfs = interfaces.clone();

	//获取com.jvm.proxy.$Proxy0的Class
        Class<?> cl = getProxyClass0(loader, intfs);  
        // 获取com.jvm.proxy.$Proxy0的构造函数
        final Constructor<?> cons = cl.getConstructor(constructorParams); 
        // 创建$Proxy0实例,并将在构造函数中执行this.h = h;
        return cons.newInstance(new Object[]{h});
    }

}

通过上面的代码,可以看出Proxy.newProxyInstance()返回的是$Proxy这个代理类的实例。

总结:

1、JDK实现动态代理,会生成$Proxy0.class。

      class $Proxy0 extends Proxy implements HelloInterface

      $Proxy0继承Proxy、实现HelloInterface

2、helloProxy = (HelloInterface) new DynamicProxy().getProxy(new HelloImpl());

      helloProxy 是$Proxy0的实例。

      $Proxy0中有一个属性h,h是DynamicProxy实例

3、helloProxy.sayHello()实际上是调用h.invoke()。h.invoke()中有我们切入的代码。

   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值