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

【JDK动态代理】JDK动态代理:为何只能代理接口和接口实现类

在Java开发中,JDK动态代理是一种非常有用的技术,它允许开发者在不修改目标类代码的情况下,为目标类添加额外的功能。然而,JDK动态代理的使用有一些限制,特别是它只能代理接口和接口实现类。本文将深入探讨这一限制的原因。

1.JDK动态代理原理

下面是一个简单的动态代理的例子


/*** 要代理的接口*/
public interface Target{String say();
}/*** 真实调用对象*/
public class RealTarget {public String invoke(){return "i'm proxy";}
}/*** JDK代理类生成*/
public class JDKProxy implements InvocationHandler {private Object target;JDKProxy(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] paramValues) {System.out.println("Before method invocation");Object result = ((RealTarget)target).invoke();System.out.println("After method invocation");return result;}
}/*** 测试例子*/
public class TestProxy {public static void main(String[] args){// 构建代理器JDKProxy proxy = new JDKProxy(new RealTarget());ClassLoader classLoader = ClassLoaderUtils.getCurrentClassLoader();// 把生成的代理类保存到文件System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");// 生成代理类Target test = (Target) Proxy.newProxyInstance(classLoader, new Class[]{Target.class}, proxy);// 方法调用System.out.println(test.say());}
}

这段代码想表达的意思就是:给 Target 接口生成一个动态代理类,并调用接口 say() 方法,但真实返回的值居然是来自 RealTarget 里面的 invoke() 方法返回值。你看,短短50行的代码,就完成了这个功能,是不是还挺有意思的?

那既然重点是代理类的生成,那我们就去看下 Proxy.newProxyInstance 里面究竟发生了什么?

一起看下下面的流程图,具体代码细节你可以对照着 JDK 的源码看(上文中有类和方法,可以直接定位),我是按照 1.7.X 版本梳理的。

在生成字节码的那个地方,也就是 ProxyGenerator.generateProxyClass() 方法里面,通过代码我们可以看到,里面是用参数 saveGeneratedFiles 来控制是否把生成的字节码保存到本地磁盘。同时为了更直观地了解代理的本质,我们需要把参数 saveGeneratedFiles 设置成true,但这个参数的值是由key为“sun.misc.ProxyGenerator.saveGeneratedFiles”的Property来控制的,动态生成的类会保存在工程根目录下的 com/sun/proxy 目录里面。现在我们找到刚才生成的 $Proxy0.class,通过反编译工具打开class文件,你会看到这样的代码: 

package com.sun.proxy;import com.proxy.Hello;
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 Target {private static Method m3;private static Method m1;private static Method m0;private static Method m2;public $Proxy0(InvocationHandler paramInvocationHandler) {super(paramInvocationHandler);}public final String say() {try {return (String)this.h.invoke(this, m3, null);} catch (Error|RuntimeException error) {throw null;} catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);} }public final boolean equals(Object paramObject) {try {return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();} catch (Error|RuntimeException error) {throw null;} catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);} }public final int hashCode() {try {return ((Integer)this.h.invoke(this, m0, null)).intValue();} catch (Error|RuntimeException error) {throw null;} catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);} }public final String toString() {try {return (String)this.h.invoke(this, m2, null);} catch (Error|RuntimeException error) {throw null;} catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);} }static {try {m3 = Class.forName("com.proxy.Target").getMethod("say", new Class[0]);m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);return;} catch (NoSuchMethodException noSuchMethodException) {throw new NoSuchMethodError(noSuchMethodException.getMessage());} catch (ClassNotFoundException classNotFoundException) {throw new NoClassDefFoundError(classNotFoundException.getMessage());} }
}

我们可以看到 $Proxy0 类里面有一个跟 Target 一样签名的 say() 方法,其中 this.h 绑定的是刚才传入的 JDKProxy 对象,所以当我们调用 Target.say() 的时候,其实它是被转发到了JDKProxy.invoke()。到这儿,整个魔术过程就透明了。 

 2.回答JDK动态代理的疑问

  1. 为何只能代理具有接口的类?
    这是因为JDK动态代理的机制所限。在Java中,动态代理通过Proxy.newProxyInstance()方法实现,此方法要求传入一个接口类作为被代理对象。这源于JDK动态代理的底层实现:它在程序运行时动态生成一个名为$Proxy0的代理类,该代理类继承自java.lang.reflect.Proxy并实现了被代理的接口。由于Java不支持多重继承,每个动态代理类都继承自Proxy,因此只能代理接口,而无法代理具体实现类。

  2. JDK动态代理能否代理类?
    JDK中的Proxy类主要用于保存动态代理的处理器InvocationHandler。理论上,如果不通过Proxy类而直接在动态生成的代理类内部设置处理器,可能实现对类的动态代理。然而,JDK的设计者并未采取这种方式,这主要是出于设计上的考虑和限制。

  3. 为何这样设计?

    • 使用场景与需求:动态代理的主要用途是在不修改原始实现的前提下,对方法进行拦截以实现功能增强或扩展。在实际开发中,基于接口编程是常见模式,因此基于接口实现动态代理符合需求和场景。当然,也存在没有实现接口的类,此时JDK动态代理无法满足需求。
    • 代码重用与扩展性:在Java中,类的设计更注重共性能力的抽象,以提高代码的重用性和扩展性。动态代理也遵循这一原则,它封装了代理类的生成逻辑、接口判断以及InvocationHandler的持有等,将这些抽象逻辑放在Proxy父类中是一个合理的选择。
  4. 其他实现方式
    对于需要代理没有接口的类,可以选择使用CGLIB动态代理。CGLIB通过动态生成被代理类的子类,并重写非final修饰的方法,在子类中拦截父类方法的调用,从而实现动态代理。这种方式弥补了JDK动态代理只能代理接口的不足。

三、总结

JDK动态代理是Java中一种强大而灵活的技术,它允许在不修改原始代码的情况下对目标对象的方法进行功能增强。然而,由于其基于接口的代理机制,它只能代理接口和接口实现类。对于需要代理没有实现接口的类的情况,可以考虑使用CGLIB动态代理等替代方案。在实际开发中,应根据具体需求选择合适的代理机制,以实现最佳的性能和可维护性。

 

相关文章:

【JDK动态代理】JDK动态代理:为何只能代理接口和接口实现类

在Java开发中,JDK动态代理是一种非常有用的技术,它允许开发者在不修改目标类代码的情况下,为目标类添加额外的功能。然而,JDK动态代理的使用有一些限制,特别是它只能代理接口和接口实现类。本文将深入探讨这一限制的原…...

MFC工控项目实例二十一型号选择界面删除参数按钮禁用切换

承接专栏《MFC工控项目实例二十手动测试界面模拟量输入实时显示》 对于禁止使用的删除、参数按钮,在选中列表控件选项时切换为能够使用。 1、在TypDlg.h文件中添加代码 #include "ShadeButtonST.h" #include "BtnST.h" class CTypDlg : publi…...

前端框架对比和选择指南

前端框架对比和选择指南 随着 Web 开发技术的快速发展,前端框架已经成为了现代 Web 开发的核心工具之一。它们为开发人员提供了快速构建高效、交互性强的应用的基础。当前流行的前端框架主要包括 React.js、Vue.js 和 Angular.js。在这篇技术博客中,我们…...

人工智能价格战——如何降低成本让人工智能更易于普及

十年前,开发人工智能 (AI) 是只有大公司和资金充足的研究机构才能负担得起的事情。必要的硬件、软件和数据存储成本非常高。但从那时起,情况发生了很大变化。一切始于 2012 年的 AlexNet,这是一种深度学习模型,展示了神经网络的真…...

企业间图文档发放:如何在保障安全的同时提升效率?

不管是大型企业,还是小型创业公司,不论企业规模大小,每天都会有大量的图文档发放,对内传输协作和对外发送使用,数据的生产也是企业业务生产力的体现之一。 伴随着业务范围的不断扩大,企业与客户、合作伙伴之…...

深入解析 ConcurrentHashMap:从 JDK 1.7 到 JDK 1.8

✨探索Java基础 ConcurrentHashMap✨ 引言 ConcurrentHashMap 是 Java 中一个线程安全的高效 Map 集合。它在多线程环境下提供了高性能的数据访问和修改能力。本文将详细探讨 ConcurrentHashMap 在 JDK 1.7 和 JDK 1.8 中的不同实现方式,以及它们各自的优缺点。 …...

VS code user setting 与 workspace setting 的区别

VS code user setting 与 workspace setting 的区别 引言正文引言 相信有不少开始接触 VS code 的小伙伴会有疑问,user setting 与 workspace setting 有什么区别呢?这里我们来说明一下 正文 首先,当我们使用 Ctrl + Shift + P 打开搜索输入 setting 后,可以弹出 4 个se…...

XPath基础知识点讲解——用于在XML中查找信息的语言

1. 什么是XPath? XPath(XML Path Language)是用于在XML(Extensible Markup Language)文档中查找信息的语言。它可以通过路径表达式来选择XML文档中的节点,类似于如何在文件系统中使用路径查找文件。XPath是…...

Visual Studio 2022

VS(Visual Studio)是一款由微软开发的集成开发环境(IDE),用于开发应用程序、网站以及移动应用等。VS的历史可以追溯到1997年,当时发布了第一个版本的VS。以下是VS的一些重要历史里程碑: Visual …...

微软Win11 22H2/23H2 九月可选更新KB5043145发布!

系统之家于9月27日发出最新报道,微软针对Windows11系统,发布了九月最新可选更新补丁KB5043145,22H2用户安装后,系统版本号升至22621.4249,23H2用户安装后升至22631.4249。本次更新修复了Edge使用IE模式有时会停止响应等…...

试试号称最好的7B模型(论文复现)

试试号称最好的7B模型(论文复现) 本文所涉及所有资源均在传知代码平台可获取 文章目录 试试号称最好的7B模型(论文复现)概述论文原理部署与复现推理微调adapter 融合 概述 Mistral 7B 是一个新型的具有 7.3 万亿参数的大语言模型。…...

CTF中文件包含

php伪协议的分类 伪协议是文件包含的基础,理解伪协议的原理才能更好的利用文件包含漏洞。 php://input php://input代表可以访问请求的原始数据,简单来说POST请求的情况下,php://input可以获取到post的数据。 使用条件:includ…...

20.指针相关知识点1

指针相关知识点1 1.定义一个指针变量指向数组2.指针偏移遍历数组3.指针偏移的补充4.指针和数组名的见怪不怪5.函数、指针、数组的结合 1.定义一个指针变量指向数组 指向数组首元素的地址 指向数组起始位置&#xff1a;等于数组名 #include <stdio.h>int main(){int ar…...

PFC和LLC的本质和为什么要用PFC和LLC电路原因

我们可以用电感和电容的特性,以及电压和电流之间的不同步原理来解释PFC(功率因数校正)和LLC(谐振变换器)。 电感和电容的基本概念 电感(Inductor): 电感是一种储存电能的组件。它的电流变化比较慢,电流在电感中延迟,而电压变化得比较快。可以把电感想象成一个“滞后…...

自定义认证过滤器和自定义授权过滤器

目录 通过数据库动态加载用户信息 具体实现步骤 一.创建数据库 二.编写secutity配置类 三.编写controller 四.编写服务类实现UserDetailsService接口类 五.debug springboot启动类 认证过滤器 SpringSecurity内置认证流程 自定义认证流程 第一步:自定义一个类继承Abstr…...

单节点集群的设置及数据写入

背景:elasticsearch单个node节点写入数据-CSDN博客 单个节点数据,如下设置参数, 在单节点集群中,设置 `gateway.recover_after_nodes` 通常是没有意义的,因为单节点集群只有一个节点,无法满足 `gateway.recover_after_nodes` 的条件。然而,如果你仍然想在单节点集群中…...

【Linux学习】【Ubuntu入门】1-2 新建虚拟机ubuntu环境

1.双击打开VMware软件&#xff0c;点击“创建新的虚拟机”&#xff0c;在弹出的中选择“自定义&#xff08;高级&#xff09;” 2.点击下一步&#xff0c;自动识别ubuntu光盘映像文件&#xff0c;也可以点击“浏览”手动选择&#xff0c;点击下一步 3.设置名称及密码后&#xf…...

自动驾驶系列—自动驾驶MCU架构全方位解析:从单核到多核的选型指南与应用实例

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…...

基于单片机多功能称重系统设计

** 文章目录 前言概要功能设计设计思路 软件设计效果图 程序文章目录 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对…...

PWA(Progressive web APPs,渐进式 Web 应用): manifest.json、 Service Worker

文章目录 引言I 什么是 PWA功能特性技术上分为三个部分安装应用II Web 应用清单将Web 应用清单文件链接到站点manifest.json字段说明III Service Worker( 缓存管理)IV 结合构建工具让项目支持 PWA应用使用插件vite-plugin-pwaworkbox-webpack-plugin插件扩展知识将 PWA 作为脱机…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝

目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为&#xff1a;一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?

在工业自动化持续演进的今天&#xff0c;通信网络的角色正变得愈发关键。 2025年6月6日&#xff0c;为期三天的华南国际工业博览会在深圳国际会展中心&#xff08;宝安&#xff09;圆满落幕。作为国内工业通信领域的技术型企业&#xff0c;光路科技&#xff08;Fiberroad&…...

【Linux】自动化构建-Make/Makefile

前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具&#xff1a;make/makfile 1.背景 在一个工程中源文件不计其数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;mak…...

实战三:开发网页端界面完成黑白视频转为彩色视频

​一、需求描述 设计一个简单的视频上色应用&#xff0c;用户可以通过网页界面上传黑白视频&#xff0c;系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观&#xff0c;不需要了解技术细节。 效果图 ​二、实现思路 总体思路&#xff1a; 用户通过Gradio界面上…...