这一次彻底搞懂JDK动态代理

JavaEdge

共 4404字,需浏览 9分钟

 · 2021-06-14


  点击上方“JavaEdge”,关注公众号

设为“星标”,好文章不错过!

动态代理 V.S 静态代理


  • Proxy类的代码被固定下来,不会因为业务的逐渐庞大而庞大

  • 可以实现AOP编程,这是静态代理无法实现的

  • 解耦,如果用在web业务下,可以实现数据层和业务层的分离

  • 动态代理的优势就是实现无侵入式的代码扩展。


静态代理这个模式本身有个大问题,若类方法数量越来越多的时候,代理类的代码量十分庞大的。所以引入动态代理

动态代理



Java中动态代理的实现的关键:

  • Proxy

  • InvocationHandler

InvocationHandler#invoke

  • method 调用的方法,即需要执行的方法

  • args 方法的参数

  • proxy 代理类的实例 


JDK动态代理




JDK动态代理模式里有个拦截器,在JDK中,只要实现了InvocationHandler接口的类就是一个拦截器类拦截器的作用控制目标对象的目标方法的执行。

拦截器的具体操作步骤:

  1. 引入类

    目标类和一些扩展方法相关的类

  2. 赋值

    调用构造器,给相关对象赋值

  3. 合并逻辑处理

    在invoke方法中把所有的逻辑结合在一起。最终决定目标方法是否被调用

示例

   

思考如下问题:



代理对象由谁产生


JVM,不像静态代理,我们得自己new个代理对象。



代理对象实现了什么接口


实现的接口是目标对象实现的接口。同静态代理中代理对象实现的接口。那个继承关系图还是相同的。代理对象和目标对象都实现一个共同的接口。就是这个接口。所以Proxy.newProxyInstance()方法返回的类型就是这个接口类型。



代理对象的方法体是什么


代理对象的方法体中的内容就是拦截器中invoke方法中的内容。

所有代理对象的处理逻辑,控制是否执行目标对象的目标方法。都是在这个方法里面处理的。




拦截器中的invoke方法中的method参数是在什么时候赋值的


在客户端,代理对象调用目标方法的时候,此实例中为:

proxyObj.business();


实际上进入的是拦截器中的invoke方法,这时拦截器中的invoke方法中的method参数会被赋值。



为啥这叫JDK动态代理


因为该动态代理对象是用JDK相关代码生成。


很多同学对动态代理一直很迷糊,在于理解错了

proxyObj.business();$Proxy0

没有发现这个 proxyObj Proxy 类之间的联系,一直好奇

最后调用的business()是怎么和  invoke() 联系上的?

invoke又怎么知道business的存在?

因为大多同学不知道 $Proxy0 类,看看下面的 $Proxy0 源码,相信你完全可以理解动态代理了。


我们虽然没有显式调用invoke,但该方法确实被执行了。


可以从newProxyInstance方法作为突破口,我们先来看一下Proxy类中newProxyInstance方法的源代码:

public static Object newProxyInstance(ClassLoader loader,                                      Class<?>[] interfaces,                                      InvocationHandler h) {    final Class<?>[] intfs = interfaces.clone();    final SecurityManager sm = System.getSecurityManager();    if (sm != null) {        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);    }
/* * 查找或生成指定的代理类 * 创建代理类$Proxy0 * $Proxy0类实现了interfaces的接口,并继承了Proxy类 */ Class<?> cl = getProxyClass0(loader, intfs);
/* * 使用指定的调用处理程序调用其构造器 */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } // 形参为InvocationHandler类型的构造器 final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); }        return cons.newInstance(new Object[]{h}); } ...}


Proxy.newProxyInstance 做了什么呢?

  • 根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理$Proxy0$Proxy0类 实现了interfaces的接口,并继承了Proxy类

  • 实例化$Proxy0,并在构造器把DynamicSubject传过去,接着$Proxy00调用父类Proxy的构造器,为h赋值

$Proxy0的源码:

package com.sun.proxy;
public final class $Proxy0 extends Proxy implements TargetInterface { private static Method m1; private static Method m3; private static Method m2; 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}); }... }
public final void business() throws { try { super.h.invoke(this, m3, (Object[])null); }... }
public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); }... }
public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); }... }
static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("com.javaedge.design.pattern.structural.proxy.dynamicproxy.jdkdynamicproxy.TargetInterface").getMethod("business"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); }... }}


接着把得到的$Proxy0实例强转成TargetInterface,并将引用赋给TargetInterface。当执行proxyObj.business(),就调用了$Proxy0类中的business()方法,进而调用父类Proxy中的h的invoke()方法。即InvocationHandler.invoke()

最后提醒Proxy#getProxyClass返回的是Proxy的Class类,而非很同学想当然认为的“被代理类的Class类”! 





目前交流群已有 800+人,旨在促进技术交流,可关注公众号添加笔者微信邀请进群


喜欢文章,点个“在看、点赞、分享”素质三连支持一下~

浏览 16
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报