当前位置: 首页 > article >正文

JDK动态代理为什么只能代理有接口的类?

JDK动态代理为什么只能代理有接口的类?

JDK 动态代理只能代理有接口的类,主要原因在于其实现机制和设计理念,以下是详细解释:

1. 基于接口实现动态代理的设计理念

  • Java 的设计理念强调面向接口编程,接口定义了一组规范或契约,实现类遵循这些规范来提供具体的实现。JDK 动态代理遵循这一理念,通过代理接口来间接代理实现该接口的类,这样可以更好地符合 Java 的编程规范和设计模式,使得代码具有更高的灵活性、可维护性和可扩展性。

2. 反射机制与接口的紧密结合

  • JDK 动态代理大量运用了 Java 的反射机制来实现代理功能。在创建代理对象时,需要通过反射获取接口的相关信息,比如接口中的方法签名等。具体来说,java.lang.reflect.Proxy类在创建代理对象时,会根据传入的接口数组来确定代理对象要实现的接口,然后利用反射动态生成一个实现了这些接口的代理类的字节码。如果没有接口,就无法明确知道要代理的对象应该遵循哪些规范,也就难以通过反射机制准确地生成符合要求的代理类。

3. 代理类生成过程的限制

  • 当使用 JDK 动态代理创建代理对象时,它实际上是在运行时动态生成一个代理类。这个代理类会实现目标对象所实现的接口,并且代理类中的方法会通过反射调用目标对象的对应方法。由于代理类是基于接口生成的,它的方法签名等都是依据接口来确定的。如果目标对象没有接口,就无法确定代理类应该具有怎样的方法结构,也就无法准确生成代理类来代理目标对象的方法。

例如,假设我们有一个简单的类MyClass,它没有实现任何接口,只有一个方法doSomething()。如果要使用 JDK 动态代理来代理这个类,在创建代理对象时,由于没有接口作为参照,Proxy类就不知道代理类应该实现哪些方法,也不知道如何通过反射去调用目标对象的方法,因为没有接口来明确这些方法的定义和规范。

4. 与 Java 类型系统的兼容性

  • Java 的类型系统是基于类和接口构建的。在动态代理场景中,代理对象需要在类型上与目标对象保持一种合理的对应关系。当代理有接口的类时,代理对象可以被视为实现了相同接口的另一个实例,这符合 Java 的类型系统逻辑。而对于没有接口的类,要创建一个在类型上与之匹配且能有效代理其方法的代理对象,在 JDK 动态代理的现有机制下是难以实现的,因为没有接口来界定这种类型上的关联和规范。

综上所述,JDK 动态代理基于 Java 的设计理念、反射机制的运用、代理类生成过程以及与 Java 类型系统的兼容性等多方面因素,决定了它只能代理有接口的类。不过,在 Java 中可以使用 CGLIB 等第三方库来实现对没有接口的类的动态代理,以满足不同的开发需求。

5. 再理解

这个问题的核心本质,是 JDK 动态代理本身的机制来决定的(如图)。

首先,在 Java 里面,动态代理是通过 Proxy.newProxyInstance()方法来实现的,它需要传入被动态代理的接口类。

之所以要传入接口,不能传入类,还是取决于 JDK动态代理的底层实现(如图)。

JDK动态代理会在程序运行期间动态生成一个代理类$Proxy0,这个动态生成的代理类会继承

java.lang.reflect.Proxy类,同时还会实现被代理类的接口 IHelloService。

在 Java中,是不支持多重继承的。而每个动态代理类都会继承 Proxy类(这也是 JDK动态代理的实现规范),所以就导致 JDK里面的动态代理只能代理接口,而不能代理实现类。

就下面这张图,简单来说,他已经继承了Proxy类,那就只能实现目标接口。

(注意,下面这张图片展示的时候,上面的图片仍然保存在一个画面里面)

我分析过动态代理的源码,发现 Proxy这个类只是保存了动态代理的处理器 InvocationHandler,如果不抽出来,直接设置到$Proxy0动态代理类里面,也是可以的。

如果这么做,就可以针对实现类来做动态代理了。作者为什么这么设计,我认为有几个方面的原因。

1.动态代理本身的使用场景或者需求,只是对原始实现的一个拦截,然后去做一些功能的增强或者扩展。而实际的开发模式也都是基于面向接口来开发,所以基于接口来实现动态代理,从需求和场景都是吻合的。当然确实可能存在有些类没有实现接口的,那这个时候,JDK动态代理确实无法满足。

2.在 Java里面,类的继承关系的设计,更多的是考虑到共性能力的抽象,从而提高代码的重用性和扩展性,而动态代理也是在做这样一个事情,它封装了动态代理类生成的抽象逻辑、判断一个类是否是动态代理类、InvocationHandler的持有等等,那么把这些抽象的公共逻辑放在 Proxy这个分类里面,很显然是一个比较正常的设计思路。

总的来说,我认为这个设计上并没有什么特别值得讨论的地方,因为我认为技术方案的设计是解决特定场景问题的。

如果一定要针对普通类来做动态代理,可以选择 cglib这个组件,它会动态生成一个被代理类的子类,子类重写了父类中所有非 final修饰的方法,在子类中拦截父类的所有方法调用从而实现动态代理。

借鉴:B站Mic 【Java面试】JDK动态代理为什么只能代理有接口的类?_哔哩哔哩_bilibili


http://www.kler.cn/a/376279.html

相关文章:

  • Automated Isotope Identification Algorithm UsingArtificial Neural Networks-论文阅读
  • Java中I/O系统中的装饰器
  • 网络安全战略论文学习笔记day1
  • Hailo-8/8L系列汇总
  • QT 周期性的杀死一个进程(软件),一分钟后自动退出
  • Python之groupby()及aggregate()方法
  • 【原创分享】生产环境JAVA中间件性能优化调优要点和案例分析
  • 面向过程与面向对象
  • nginx-proxy-manager实现反向代理+自动化证书(实战)
  • 前端项目【本科期间】
  • 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-27
  • uniapp 小程序 H5 app 价格计算 避免精度丢失
  • 深入探讨 Tantivy 及其在 Milvus 中的应用:倒排索引库对比与选择
  • Android Studio开发学习(五)———LinearLayout(线性布局)
  • 微信小程序 uniapp 腾讯地图的调用
  • 设计模式之责任链的通用实践思考
  • C语言静态库
  • 数据结构之链式结构二叉树的实现(初级版)
  • FRIDA-JSAPI:Process使用
  • HTTP 405 Method Not Allowed:解析与解决
  • 【spark】——spark面试题(1)
  • 基于YOLO11/v10/v8/v5深度学习的农作物类别检测与识别系统设计与实现【python源码+Pyqt5界面+数据集+训练代码】
  • Spring Cloud Config快速入门Demo
  • 河北冠益荣信科技公司洞庭变电站工程低压备自投装置的应用
  • Android -- (静态广播) APP 监听U盘挂载
  • Android Jetpack Compose 现有Java老项目集成使用compose开发