动态代理和静态代理

代理模式(Proxy Pattern):通过创建代理对象来控制对另一个对象的访问,通常用于实现横切关注点(cross-cutting concerns),例如日志记录、系统安全性检查等。常见的代理模式就是动态代理和静态代理。

一、静态代理

静态代理:在编译时就已经确定代理关系,也就是在编译时创建代理对象。在静态代理中,代理类和被代理类通常是通过接口或继承关系来建立联系,并且代理类需要实现与被代理类相同的接口,以便能够对外提供相同的服务。

静态代理的简单实现步骤如下:

  1. 定义一个接口(或抽象类),声明代理类和被代理类需要实现的方法。
  2. 创建一个被代理类,实现接口并提供具体的实现逻辑。
  3. 创建一个代理类,实现接口并在内部持有一个被代理类的引用。
  4. 在代理类的方法中,调用被代理类的相应方法,可以在调用前后进行额外的处理。

实例:用户登录代理认证

// 接口
interface UserService {//目标方法void login(String username, String password);
}// 被代理类
class UserServiceImpl implements UserService {//具体实现public void login(String username, String password) {System.out.println("用户:" + username + " 登录成功.");}
}// 静态代理类
class AuthProxy implements UserService {//引用private UserService userService;//初始化public AuthProxy(UserService userService) {this.userService = userService;}//增强方法public void login(String username, String password) {//在调用目标方法前增强逻辑if (authenticate(username, password)) {userService.login(username, password);//原始方法} else {System.out.println("身份认证失败: " + username);}}private boolean authenticate(String username, String password) {// 简单实现身份验证逻辑return username.equals("alice") && password.equals("123");}
}// 简单测试
public class Main{public static void main(String[] args) {UserService userService = new AuthProxy(new UserServiceImpl());userService.login("alice", "123"); // 身份验证成功userService.login("john", "123");    // 身份验证失败}
}

静态代理的优点:简单易懂、编码方便,可以在代理类中灵活添加额外的功能。

静态代理的缺点:代理类和被代理类之间的关系在编译时就确定了,如果有多个类需要代理,就需要编写多个代理类,导致代码冗余。

二、动态代理

动态代理:在运行时动态生成代理类,运行时才创建代理对象。

相比于静态代理,动态代理更加灵活,可以在运行时决定代理类和被代理类的关系,无需提前编写代理类。Java中实现动态代理的关键类是Proxy和InvocationHandler。Proxy类提供了创建动态代理类的方法,而InvocationHandler接口则需要自定义一个实现类,用于处理代理类的方法调用。

动态代理的简单实现步骤如下:

  1. 定义一个接口,声明代理类和被代理类需要实现的方法。
  2. 创建一个InvocationHandler的实现类,实现invoke方法,在该方法中定义对被代理类方法的处理逻辑。
  3. 使用Proxy类的静态方法newProxyInstance创建动态代理类的实例,传入ClassLoader、接口列表和InvocationHandler实例。
  4. 调用动态代理类的方法,实际上会调用InvocationHandler中的invoke方法来处理方法调用。

实例:(和上面静态代理一样)

// 接口
interface UserService {//目标方法void login(String username, String password);
}// 被代理类
class UserServiceImpl implements UserService {//具体实现public void login(String username, String password) {System.out.println("用户:" + username + " 登录成功.");}
}// 实现类,也就是工具类
class AuthenticationHandler implements InvocationHandler {private Object target;public AuthenticationHandler (Object target) {this.target = target;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (authenticate(args[0].toString(), args[1].toString())) {return method.invoke(target, args);} else {System.out.println("身份认证失败: " + args[0]);return null;}}private boolean authenticate(String username, String password) {return username.equals("alice") && password.equals("123");}
}// 简单测试
public class Main{public static void main(String[] args) {UserService userService = new UserServiceImpl();//创建代理对象,参数有类加载器、被代理对象、代理对象UserService proxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),userService.getClass().getInterfaces(),new AuthenticationHandler (userService));proxy.login("alice", "123"); // 身份验证成功proxy.login("john", "123");    // 身份验证失败}
}

动态代理优点:在运行时决定代理类和被代理类的关系,无需提前编写代理类,减少了代码冗余。

动态代理缺点:相比于静态代理,它在运行时会带来一定的性能开销。

详细内容链接:Java动态代理

三、动态代理和静态代理的区别

  1. 实现方式:静态代理在编译时就确定代理关系,而动态代理是在运行时动态生成代理类。

  2. 灵活性:静态代理的关系在编译时确定,无法动态修改,而动态代理可以在运行时决定代理关系。

  3. 代码复用:动态代理相比静态代理具有更好的代码复用性。在静态代理中,每个被代理类都需要编写一个对应的代理类,导致代码冗余。而在动态代理中,可以通过一个通用的InvocationHandler实现类来处理多个被代理类的方法调用,减少了代码的冗余。

  4. 性能开销:动态代理在运行时通过反射机制动态生成代理类,相比静态代理会带来一定的性能开销。每次方法调用都需要通过反射调用InvocationHandler中的invoke方法,在方法调用频繁的情况下可能会影响性能。而静态代理在编译时就确定了代理关系,直接调用被代理类的方法,性能更高。

应用场景:

  • 静态代理适用于代理类数量较少且固定的情况,可以在编译时确定代理关系,并在代理类中添加额外的功能。例如,日志记录、权限校验等横切关注点可以通过静态代理来实现。
  • 动态代理适用于代理类数量较多或不确定的情况,可以在运行时动态生成代理类,并通过InvocationHandler实现类来处理不同的代理逻辑。例如,AOP(面向切面编程)中的方法拦截、事务管理等功能就是通过动态代理来实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/238050.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux磁盘空间不足扩展

先在虚拟机Vmware上扩展磁盘空间 后将fdisk 进行分区之后,在/dev/中找不到新分区文件 3.创建物理卷pv时发现找不到/dev/sda3分区,通过ls查看确认在/dev/中没有找到新分区文件 4.解决方法 执行:partprobe 再查看/dev中是否可以看到新分区文件…

QT计算时间差 秒 毫秒 (另附c++版本)

QT计算时间差,两次时间的间隔 项目计算码流需要用到计算时间差 QT版本 利用secsTo函数 QDateTime startTime QDateTime::currentDateTime();Sleep(5000);QDateTime endTime QDateTime::currentDateTime();qint64 intervalTime startTime.secsTo(endTime); //求…

云轴科技ZStack 英特尔联合成立云系统解决方案创新中心

近日,云轴科技ZStack 与英特尔联合成立云系统解决方案创新中心,并完成揭牌仪式。双方将基于多年合作基础进一步加快研发下一代创新技术和解决方案,为客户和合作伙伴提供更高效、安全的云基础设施解决方案。 左三:云轴科技ZStack 联合创始人兼…

量化交易学习笔记:XGBoost 在量化选股中的应用

一、引言 本篇文章通过借鉴传统机器学习算法——XGBoost——对相同的量价因子进行实验,方便与深度学习模型进行对比实践。 二、算法介绍 XGBoost 是在 Gradient Boosting(梯度提升)框架下实现的机器学习算法,全称为“极限梯度提…

FPGA模块——以太网(1)MDIO读写

FPGA模块——以太网MDIO读写 MDIO接口介绍MDIO接口代码(1)MDIO接口驱动代码(2)使用MDIO驱动的代码 MDIO接口介绍 MDIO是串行管理接口。MAC 和 PHY 芯片有一个配置接口,即 MDIO 接口,可以配置 PHY 芯片的工…

树的重心(dfs深度搜索)

树的重心 原题链接:846. 树的重心 - AcWing题库 邻接表存储树图 模板代码 void add(int a, int b){e[id] b,ne[id] h[a], h[a] id; }dfs 搜索树 模板代码 void dfs(int u){f[u] true;for(int i h[u]; i!-1; i ne[i]){int j e[i];if(!f[j])dfs(j);} }整体…

理解AI思维链:AI领域的核心概念及其意义

理解AI思维链:AI领域的核心概念及其意义 引言AI思维链的定义AI思维链的重要性实际应用案例分析面临的挑战与未来展望结语 引言 在这个日益由数据驱动的时代,人工智能(AI)已经成为科技领域的一颗耀眼的明星,其影响力遍…

java UDP编程

UDP协议是一个不安全、不连续的,把数据发送出去之后就结束了,根本不管对方有没有接收到。 快递员:DatagramSocket 包裹:DatagramPacket 原理就是将数据以及对方的信息都放到包裹里面,然后让快递员发送给对应的人。…

(八)STM32 USART —— 串口通讯

目录 1. 串口通讯协议简介 1.1 物理层 1.1.1 电平标准 1)TTL 电平 2)RS-232 电平 3)RS-485 电平 4)CAN 总线电平 1.1.2 USB 和 串口 的区分 1.1.3 RS-232 信号线 1.2 协议层 1)波特率 2)通讯…

负载均衡:一致性哈希解决了哪些问题?

在业务开发中,缓存服务和其他数据服务一样,需要满足高可用性,而高可用最常用的手段就是集群扩展。 缓存的集群高可用 目前 Redis 流行的集群方案有 官方 Cluster 方案、twemproxy 代理方案、哨兵模式、Codis 等方案,关于这几种方…

Java 自定义泛型

1、接口的泛型 例如List<数据类型>&#xff0c;在创建接口的时候可以通过传不同的类型&#xff0c;进行使用。 如果需要对一些类型进行一些相同的类似于增删改查的操作&#xff0c;那么可以用泛型来简化&#xff0c;只需要将需要操作的类型传入即可。 需要注意的是泛型…

阿里云赵大川:弹性计算推理解决方案拯救 AIGC 算力危机

云布道师 本篇文章围绕弹性计算推理解决方案 DeepGPU 实例如何支持 Stable Diffusion 文生图推理、Stable Diffusion 推理演示示例等相关话题展开。 赵大川 阿里云弹性计算高级技术专家 GPU 云服务器推理解决方案的提出背景 随着 AIGC 时代的到来&#xff0c;两个重要应用应…

js如何调用iframe页面里的方法

document.getElementById("iframeID").contentWindow.子级页面方法(); <body><h3>父页面</h3><iframe id"iframebb" src"b.html" ></iframe><br><script>function ff(){alert("这里是父页面ff的…

IDEA版SSM入门到实战(Maven+MyBatis+Spring+SpringMVC) -SpringMVC @RequestMapping详解

第一章 RequestMapping详解 RequestMapping注解作用&#xff1a;为指定的类或方法设置相应URL 1.1 RequestMapping注解位置 书写在类上面 作用&#xff1a;为当前类设置映射URL注意&#xff1a;不能单独使用&#xff0c;需要与方法上的RequestMapping配合使用 书写在方法上面 …

vi和vim的区别

目录 一、前言 二、vi/vim 的介绍 三、Vi/Vim 常见指令 四、vi和vim的区别 一、前言 写这篇文章的目的&#xff0c;是为了告诉大家我们如果要在终端下对文本进行编辑和修改可以使用vim编辑器。 Ubuntu 自带了 VI 编辑器&#xff0c;但是 VI 编辑器对于习惯了 Windows 下进…

Python办公自动化 – 日志分析和自动化FTP操作

Python办公自动化 – 日志分析和自动化FTP操作 以下是往期的文章目录&#xff0c;需要可以查看哦。 Python办公自动化 – Excel和Word的操作运用 Python办公自动化 – Python发送电子邮件和Outlook的集成 Python办公自动化 – 对PDF文档和PPT文档的处理 Python办公自动化 – 对…

55 回溯算法解黄金矿工问题

问题描述&#xff1a;你要开发一座金矿&#xff0c;地质学家已经探明了这座金矿中的资源分布&#xff0c;并用大小为m*n的网格grid进行了标注&#xff0c;每个单元格中的整数就表示这一单元格中的黄金数量&#xff1b;如果单元格是空的&#xff0c;那么就是0&#xff0c;为了使…

【pentaho】kettle读取Hive表不支持bigint和timstamp类型解决。

一、bigint类型 报错: Unable to get value BigNumber(16) from database resultset显示kettle认为此应该是decimal类型(kettle中是TYPE_BIGNUMBER或称BigNumber)&#xff0c;但实际hive数据库中是big类型。 修改kettle源码解决&#xff1a; kettle中java.sql.Types到kettle…

MFC使用高速绘图控件high-speed Charting Control绘制柱形图

1. 创建MFC单文档工程BarChartDemo。 2. 在工程文件夹下新建文件夹ChartCtrl,将ChartCtrl源码放入,如下图所示。在工程中添加这些项,项目——添加——现有项,全部添加。 3. 添加一个对话框,ID为IDD_DLG_BAR,类名为CBarDlg。 4. 在对话框中添加Custom Control控件,将控…

【SpringBoot应用篇】【AOP+注解】SpringBoot+SpEL表达式基于注解实现权限控制

【SpringBoot应用篇】【AOP注解】SpringBootSpEL表达式基于注解实现权限控制 Spring SpEL基本表达式类相关表达式表达式模板 SpEL表达式实现权限控制PreAuthAuthFunPreAuthAspectUserControllerSpelParserUtils Spring SpEL Spring 表达式语言 SpEL 是一种非常强大的表达式语言…