文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-12-03 02:20

关注

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

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

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

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

动态代理

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

InvocationHandler#invoke

JDK动态代理

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

拦截器的具体操作步骤:

引入类

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

赋值

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

合并逻辑处理

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

示例

思考如下问题:

代理对象由谁产生

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

代理对象实现了什么接口

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

代理对象的方法体是什么

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

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

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

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

  1. proxyObj.business(); 

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

为啥这叫JDK动态代理

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

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

  1. proxyObj.business(); 
  2. $Proxy0 

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

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

invoke又怎么知道business的存在?

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

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

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

  1. public static Object newProxyInstance(ClassLoader loader, 
  2.                                       Class[] interfaces, 
  3.                                       InvocationHandler h) { 
  4.     final Class[] intfs = interfaces.clone(); 
  5.     final SecurityManager sm = System.getSecurityManager(); 
  6.     if (sm != null) { 
  7.         checkProxyAccess(Reflection.getCallerClass(), loader, intfs); 
  8.     } 
  9.  
  10.      
  11.     Class cl = getProxyClass0(loader, intfs); 
  12.  
  13.      
  14.     try { 
  15.         if (sm != null) { 
  16.             checkNewProxyPermission(Reflection.getCallerClass(), cl); 
  17.         } 
  18.         // 形参为InvocationHandler类型的构造器 
  19.         final Constructor cons = cl.getConstructor(constructorParams); 
  20.         final InvocationHandler ih = h; 
  21.         if (!Modifier.isPublic(cl.getModifiers())) { 
  22.             AccessController.doPrivileged(new PrivilegedAction() { 
  23.                 public Void run() { 
  24.                     cons.setAccessible(true); 
  25.                     return null
  26.                 } 
  27.             }); 
  28.         } 
  29.         return cons.newInstance(new Object[]{h}); 
  30.     } ... 

Proxy.newProxyInstance 做了什么呢?

$Proxy0的源码:

  1. package com.sun.proxy; 
  2.  
  3. public final class $Proxy0 extends Proxy implements TargetInterface { 
  4.     private static Method m1; 
  5.     private static Method m3; 
  6.     private static Method m2; 
  7.     private static Method m0; 
  8.  
  9.     public $Proxy0(InvocationHandler var1) throws  { 
  10.         super(var1); 
  11.     } 
  12.  
  13.     public final boolean equals(Object var1) throws  { 
  14.         try { 
  15.             return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); 
  16.         }... 
  17.     } 
  18.  
  19.     public final void business() throws  { 
  20.         try { 
  21.             super.h.invoke(this, m3, (Object[])null); 
  22.         }... 
  23.     } 
  24.  
  25.     public final String toString() throws  { 
  26.         try { 
  27.             return (String)super.h.invoke(this, m2, (Object[])null); 
  28.         }... 
  29.     } 
  30.  
  31.     public final int hashCode() throws  { 
  32.         try { 
  33.             return (Integer)super.h.invoke(this, m0, (Object[])null); 
  34.         }... 
  35.     } 
  36.  
  37.     static { 
  38.         try { 
  39.             m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); 
  40.             m3 = Class.forName("com.javaedge.design.pattern.structural.proxy.dynamicproxy.jdkdynamicproxy.TargetInterface").getMethod("business"); 
  41.             m2 = Class.forName("java.lang.Object").getMethod("toString"); 
  42.             m0 = Class.forName("java.lang.Object").getMethod("hashCode"); 
  43.         }... 
  44.     } 

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

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

本文转载自微信公众号「JavaEdge」,可以通过以下二维码关注。转载本文请联系JavaEdge公众号。

 

来源:JavaEdge内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯