【设计模式】代理模式详解
1.简介
代理模式是常用的Java设计模式,该模式的特点是代理类与委托类共享相同的接口。代理类主要负责预处理消息、过滤消息、将消息转发给委托类,并在事后处理消息等。代理类与委托类之间通常存在关联关系,一个代理类对象与一个委托类对象关联。代理类对象本身不真正实现服务,而是通过调用委托类对象的相关方法来提供特定的服务。
代理模式主要包括以下角色:
- 抽象主题(Subject):定义代理类和委托类(RealSubject)的共同接口。这个接口规定了代理类和委托类必须实现的方法,代理类可以通过这个接口来调用委托类的方法。
- 真实主题(RealSubject):实现抽象主题,定义委托类的操作。它包含了实际的业务逻辑,是客户端实际需要调用的对象。
- 代理类(Proxy):实现抽象主题,持有对委托类的引用,并在其方法被调用时进行控制。代理类在调用委托类的方法前后可以添加一些额外的功能,如日志记录、权限控制、事务处理等。
2.静态代理
静态代理: 在编译时期确定代理类和目标类的关系,代理类和目标类都要实现同一个接口。
定义一个简单的例子:假如一个租客需要租房子,他可以直接通过**房东(委托类)去租房,也可以经过中介(代理类)**去租房。房东(realsubject)和中介(proxy)都需要实现subject接口实现房子出租。
- 确定接口具体行为
首先创建一个Person接口。这个接口是房东和中介的共同接口,租房行为可以被中介代理。
public interface Person {// 出租房子void hire();
}
- 编写委托类业务逻辑
创建一个委托类,实现subject接口,并编写业务逻辑
public class Landlord implements Person{// 房东直售@Overridepublic void hire() {System.out.println("出租,收款1000元");}
}
- 代理类增强方法
创建一个代理类,同样实现subject接口,对委托类的方法进行增强
public class Agency implements Person{private final Landlord landlord;public Agency(Landlord landlord) {this.landlord = landlord;}// 中介出租,额外收取费用@Overridepublic void hire() {System.out.println("开始办理租房手续");landlord.hire();System.out.println("额外收取中介费200元");}
}
- 测试类使用代理对象
public class Main {public static void main(String[] args) {// 获取代理对象Landlord landlord = new Landlord();Agency agency = new Agency(landlord);// 使用代理方法agency.hire();}
}
输出结果如下,可以发现对方法进行了增强
3.动态代理
动态代理: 在程序运行时动态生成代理类(subject的实现类)。
相比于静态代理, 动态代理的优势在于其较高的灵活性和代码复用性。同一个动态代理处理器可以代理多个目标对象,而静态代理则需要创建大量的代理类。
在Java中,可以通过JDK和CGLIB实现动态代理。
3.1.JDK动态代理
实现原理
JDK动态代理: 使用反射机制
来实现动态代理,代理类实现了一个或多个接口,并在运行时生成代理实例。在java的java.lang.reflect
包下提供了Proxy
类和InvocationHandler
接口,利用这两个类和接口,可以在运行时动态生成指定接口的实现类。
Proxy
类就是用来创建一个代理对象的类,在JDK动态代理中我们需要使用其newProxyInstance
方法。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
这个方法的作用就是创建一个代理类对象,它接收以下三个参数:
- loader:一个ClassLoader对象,指定哪个ClassLoader将加载生成的代理类。
- interfaces:一个Interface对象数组,定义代理对象实现的一组接口,代理类可以调用这些接口中声明的所有方法。
- h:一个InvocationHandler对象,指定代理对象的方法调用将关联到哪个InvocationHandler对象,由它处理实际的方法调用。
InvocationHandler
接口提供了一个invoke
方法,当代理对象调用方法时,invoke
方法会被调用。通过实现这个接口,可以在方法调用前后添加自定义逻辑。
/*** proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0* method:我们所要调用某个对象真实的方法的Method对象* args:指代代理对象方法传递的参数*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
代码实现
- 确定接口具体行为
这里我们设计两个接口
public interface HouseServiceA {// 出租房子void hire();
}
public interface HouseServiceB {// 转租房子void sublet();
}
- 编写委托类业务逻辑
委托类实现这两个接口,并且定义具体的业务逻辑
public class HouseServiceImpl implements HouseServiceA, HouseServiceB {@Overridepublic void hire() {System.out.println("出租房子");}@Overridepublic void sublet() {System.out.println("转租房子");}
}
- 编写代理工厂代码
代理工厂负责在运行时动态生成代理类,需要实现InvocationHandler
接口重写invoke
方法来做方法增强,使用Proxy
类创建代理对象。
/*** JDK动态代理实现InvocationHandler接口*/
public class HouseFactory implements InvocationHandler {private Object target;//定义获取代理对象的方法(将目标对象传入进行代理)public Object getJDKProxy(Object target){//为目标对象target赋值this.target = target;//JDK动态代理只能针对实现了接口的类进行代理,因此需要传递接口的classreturn Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class<?>[]{HouseServiceA.class, HouseServiceB.class},this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("JDK动态代理开始");// 调用invoke方法,result存储该方法的返回值Object result = method.invoke(target, args);System.out.println("JDK动态代理结束");return result;}
}
- 测试类使用代理对象
在实际使用时,只需要将工厂类生成的代理对象转为需要的代理类,即可实现同时代理多个接口的方法。
public class Main {public static void main(String[] args) {// 创建代理类工厂HouseFactory factory = new HouseFactory();// 动态生成接口A的代理类HouseServiceA houseProxyA = (HouseServiceA) factory.getJDKProxy(new HouseServiceImpl());houseProxyA.hire();System.out.println("=====================");// 动态生成接口B的代理类HouseServiceB houseProxyB = (HouseServiceB) factory.getJDKProxy(new HouseServiceImpl());houseProxyB.sublet();}
}
返回结果:
3.2.CGLIB动态代理
实现原理
CGLIB动态代理: 通过生成字节码
来实现动态代理,代理类继承自目标类,并在运行时生成字节码。依赖于ASM下的Enhancer
类和MethodInterceptor
接口,可以在运行时动态生成目标类的子类。
Enhancer
类是用来创建代理对象的类。在CGLIB动态代理中,我们需要使用其create
方法。
public class Enhancer {public Object create();// 其他方法
}
这个方法的作用是创建一个代理类对象,通常还需要设置以下几个属性:
- setSuperclass:设置被代理的目标类,CGLIB通过生成目标类的子类来实现代理。
- setCallback:设置回调接口,用于处理代理对象的方法调用。
MethodInterceptor
接口提供了一个intercept
方法,当代理对象调用方法时,intercept
方法会被调用。通过实现这个接口,可以在方法调用前后添加自定义逻辑。
/*** obj: 代理对象* method: 被代理的方法* args: 方法的参数* proxy: 用于调用父类方法的代理*/
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable;
与JDK动态代理不同,CGLIB代理不需要目标类实现接口。CGLIB通过生成目标类的子类并重写方法来实现代理,因此它可以代理没有实现接口的类。
代码实现
- 首先导入依赖
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
- 编写委托类业务逻辑(无需实现接口)
public class HouseServiceImpl {public void hire() {System.out.println("出租房子");}public void sublet() {System.out.println("转租房子");}
}
- 测试类使用代理对象
public class Main {public static void main(String[] args) {HouseFactory factory = new HouseFactory();HouseServiceImpl cglibProxy = (HouseServiceImpl) factory.getCglibProxy(new HouseServiceImpl());cglibProxy.hire();System.out.println("=====================");cglibProxy.sublet();}
}
返回结果:
3.3.性能对比
Cglib 动态代理的性能通常比 JDK 动态代理要好,因为它直接生成字节码
来实现方法调用,而不是通过反射机制
。具体原因如下:
方法调用的开销
- JDK 动态代理:使用反射机制来调用方法。反射机制虽然强大,但由于它在运行时需要解析和执行方法调用,因此存在一定的性能开销。这种开销包括方法查找、访问权限检查和参数处理等。
- Cglib 动态代理:直接生成目标类的子类,并通过字节码操作来实现方法调用。这样的方法调用与普通的 Java 方法调用类似,省去了反射机制的开销。直接生成字节码并在运行时加载和执行,这样的方法调用更加高效。
字节码生成与执行
- JDK 动态代理:在每次方法调用时都需要通过反射来进行,这意味着每次调用都会有反射的开销。
- Cglib 动态代理:通过字节码生成技术,将代理逻辑直接编译成字节码,并在类加载时一次性生成代理类。这种方式只在类加载时有一次性开销,而后续的每次方法调用都不再有反射的额外开销,因而性能更好。
内联优化
- JDK 动态代理:由于使用反射,JVM 很难对反射调用进行内联优化。方法内联是 JIT优化机制的一种,可以将方法调用直接替换为方法体,以减少方法调用的开销。
- Cglib 动态代理:生成的字节码与普通的 Java 方法调用无异,因此 JVM 可以对这些方法调用进行内联优化,从而进一步提升性能。
4.总结
静态代理
实现方式:
- 由程序员显式编写代理类。代理类在编译期确定,编译前就存在代理类的字节码文件。
- 需要实现与目标对象相同的接口,且在代理类中显式调用目标对象的方法。
优点:
- 结构简单,容易理解。
缺点:
- 每增加一个接口,都需要编写对应的代理类,代码量大,维护成本高。静态代理类在编译期生成,灵活性差。
JDK动态代理
实现方式:
- 使用
java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口。 - 代理类在运行时动态生成,不需要显式编写代理类。
优点:
- 代理类在运行时生成,增加了代码的灵活性和可维护性。
缺点:
- 只能代理实现了接口的类,不能代理没有实现接口的类。
CGLIB动态代理
实现方式:
- 使用CGLIB(Code Generation Library),依赖ASM字节码生成框架。
- 代理类在运行时动态生成,不需要显式编写代理类。
优点:
- 不要求目标类实现接口,可以代理普通的类。
- 性能通常比JDK动态代理更高,尤其在代理大量方法调用时更为显著。
缺点:
- 不能代理
final
类和final
方法。
适用场景:
-
静态代理:需要手动编写代理类,适用于简单的场景,但不够灵活,维护成本高。
-
JDK动态代理:适用于实现了接口的类,代理类在运行时生成,灵活性高,但只能代理接口。
-
CGLIB动态代理:适用于没有实现接口的类,性能优于JDK动态代理,但不能代理
final
类和final
方法,且使用复杂度稍高。
相关文章:
【设计模式】代理模式详解
1.简介 代理模式是常用的Java设计模式,该模式的特点是代理类与委托类共享相同的接口。代理类主要负责预处理消息、过滤消息、将消息转发给委托类,并在事后处理消息等。代理类与委托类之间通常存在关联关系,一个代理类对象与一个委托类对象关…...
Python变量和简单的数据类型
1、变量 massageHello python world! print(massage) massageHello world print(massage) 运行这个代码发现,同一个变量出现两个不同的结果 Hello python world! Hello world 在程序中,可随时修改变量的值&…...
切比雪夫距离
切比雪夫距离(Chebyshev Distance),又称棋盘距离或最大值距离,是一种用于测量两个点之间距离的度量方法。在二维平面上,切比雪夫距离定义为两个点之间的最大坐标差值。其公式如下: DChebyshevmax(∣x2−…...
计算机基础(Windows 10+Office 2016)教程 —— 第4章 计算机网络与Internet(下)
第4章 计算机网络与Internet 4.4 局域网4.4.1 局域网概述4.4.2 以太网4.4.3 令牌环网4.4.4 无线局域网 4.5 Internet4.5.1 Internet 概述4.5.2 Internet 的基本概念4.5.3 Internet 的接入4.5.4 万维网 4.6 Internet的应用4.6.1 电子邮件4.6.2 文件传输4.6.3 搜索引擎 4.4 局域网…...
机器学习用Python还是R?哪个更好一些?
选择使用Python还是R来进行机器学习取决于多个因素,包括个人偏好、项目需求以及可用的资源。这里我可以简要比较一下它们的优缺点: Python的优势: 通用性和灵活性: Python是一种通用编程语言,可以用于多种用途&#…...
4个自定义倒计时
<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><title>4个自定义倒计时</title><style>* {margin: 0;padding: 0;box-sizing: border-box;user-select: none;body {background: #0b1b2c;}}hea…...
linux系统编程中Shell脚本配置,及linux脚本中的man test
Shell脚本配置是指在脚本中设置各种参数、选项和环境,以确保脚本能够根据预期的需求和环境执行。配置可以包括变量设置、环境变量、命令选项和错误处理等。 1. 脚本开头的配置 Shebang 第一行通常是shebang,它告诉系统使用哪个解释器来执行脚本。例如…...
Win7虚拟机分享(已安装VMware Tools)
前言 之前写过VMware安装Win7并安装VMware tools的博客,但操作仍显繁琐。后来发现可以直接分享已经配置好的虚拟机,所有软件都是安装好的,解压即用。 一. VMware Win7虚拟机配置 已完成的配置和安装的软件 专业版Win7系统(已永久激活)VMware…...
CANOpen EMCY紧急报文介绍
什么是CANOpen紧急报文 CANOpen中的Emcy紧急报文用于当设备出现故障或警告时,向其它节点报告故障或警告使用的。如设备某个设备出现过压或过流时,就可以发送紧急报文。 紧急报文的格式 错误代码:是0x1003索引预定义错误字段的内容ÿ…...
JAVA项目
目录 一、前言 二、技术介绍 三、项目实现流程 四、论文流程参考 五、核心代码截图 专注于大学生实战开发、讲解和毕业答疑等辅导,获取源码后台 一、前言 在数字化音乐时代,个性化推荐已成为提升用户体验、促进音乐消费的重要手段。为此࿰…...
️ LangChain +Streamlit+ Llama :将对话式人工智能引入您的本地设备(下篇)
引言:种下一棵树最好的时间是十年前,其次是现在 书接上回:将对话式人工智能引入您的本地设备成为可能CSDNhttps://mp.csdn.net/mp_blog/creation/editor/140865426 目的:在这个大模型横行的时候,我们常用电脑如何开展大模型的工作…...
Kafka实战(Scala操作)
Kafka基础讲解部分 Kafka基础讲解部分 Kafka实战(Scala操作) 1、引入依赖 版本: <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.report…...
Android Framework 之WMS详解
1.WMS说的就是 WindowManagerService:负责为Activity对应的窗口分配Surface,管理Surface的显示顺序以及位置尺寸,控制窗口动画 。 它是Android系统中为各个客户端即每个app来提供这样的服务的一个类。 在Android系统中在systemServer 进程和各…...
opencv-图像仿射变换
仿射变换设计图像位置角度的变化,是深度学习预处理中常用的功能。仿射变换就是对图像的平移缩放旋转翻转操作的组合 如下图,对图中点1,2,3与图二中三个点一一映射,仍然形成三角形,但形状已经发生改变,通过这两组三点求…...
算法的基本概念
一、算法的基本概念思维导图 二、什么是算法: 1.我们知道数据结构就是将我门现实的世界中的问题数据化,存入计算机中,并实现对数据结构的一些基本操作。 2.算法就是如何处理这些存入计算机中的信息,以求高效的解决实际问题。 3…...
124. Go Template应用实例:用代码生成代码
文章目录 生成器模式生成器代码生成 本文用生成器模式作为例子,来演示如何用代码生成代码。 生成器模式 熟悉 Java 开发的同学都知道,lombok 有一个著名的注解 Builder ,只要加在类上面,就可以自动生成 Builder 模式的代码。如下…...
【AI实践】阿里云方言文本转语音TTS
最近要做一些普通话和方言demo 找一个免费工具 免费在线文字转语音工具 | edge-tts 在线体验 (bingal.com) 还有一些方言在阿里云上找了下,基于官方demo改了一下 阿里云语音合成接口说明_智能语音交互(ISI)-阿里云帮助中心 (aliyun.com) 如何下载安装、使用语音…...
java 之 各类日期格式转换
一、前言 大家在开发过程中必不可少得和日期打交道,对接别的系统时,时间日期格式不一致,每次都要转换! 从 Java1 到 Java8 将近 20 年,再加上 Java8 的普及时间、各种历史 API 兼容过渡时间。我们很多时候需要在旧时间 API 与新时…...
Nvidia黄仁勋对话Meta扎克伯格:AI和下一代计算平台的未来 | SIGGRAPH 2024对谈回顾
在今年的SIGGRAPH图形大会上,Nvidia创始人兼CEO黄仁勋与Meta创始人马克扎克伯格进行了一场长达60分钟的对谈。这场对话不仅讨论了AI的未来发展和Meta的开源哲学,还发布了不少新产品,并深入探讨了下一代计算平台的可能性。 引言 人工智能的发…...
【JAVA设计模式】适配器模式——类适配器模式详解与案例分析
前言 在软件设计中,适配器模式(Adapter Pattern)是一种结构型设计模式,旨在使不兼容的接口能够协同工作。它通过引入一个适配器类,帮助两个接口之间进行适配,使得它们能够互相操作。本文将详细介绍适配器模…...
【Vue】全局组件和局部组件
一、全局组件 定义: 全局组件是在整个Vue应用中都可以使用的组件。它们被注册在Vue的根实例上,因此可以在任何子组件的模板中被引用,而无需在每个组件中重复注册。 注册方式: 全局组件通过Vue.component方法进行注册。这个方法接…...
react引入高德地图并初始化卫星地图
react引入高德地图并初始化卫星地图 1.安装依赖 yarn add react-amap amap/amap-jsapi-loader2.初始化地图 import AMapLoader from "amap/amap-jsapi-loader"; import { FC, useEffect, useRef, useState } from "react";const HomeRight () > {con…...
2024最简七步完成 将本地项目提交到github仓库方法
2024最简七步完成 将本地项目提交到github仓库方法 文章目录 2024最简七步完成 将本地项目提交到github仓库方法一、前言二、具体步骤1、github仓库创建2、将远程仓库拉取并合并(1)初始化本地仓库(2)本地仓库与Github仓库关联&…...
前端WebSocket入门,看这篇就够啦!!
在HTML5 的早期开发过程中,由于意识到现有的 HTTP 协议在实时通信方面的不足,开发者开始探索能够在 Web 环境下实现双向实时通信的新的通信协议,提出了 WebSocket 协议的概念。 一、什么是 WebSocket? WebSocket 是一种在单个 T…...
漏洞复现-F6-11泛微-E-Cology-SQL
本文来自无问社区,更多漏洞信息可前往查看http://www.wwlib.cn/index.php/artread/artid/15575.html 0x01 产品简介 泛微协同管理应用平台e-cology是一套企业级大型协同管理平台 0x02 漏洞概述 该漏洞是由于泛微e-cology未对用户的输入进行有效的过滤࿰…...
Turbo Boost 禁用
最近在做OAI NR的时候关闭CPU 睿频的时候出了一些问题,这里我把我找到的资料记录一下: 禁用 Turbo Boost 的过程可能会因不同的 BIOS/UEFI 和操作系统设置而有所不同。以下是一些可能的原因及解决方法: 可能的原因 BIOS/UEFI 设置问题: 你的…...
假期BUUCTF小练习3
文章目录 [极客大挑战 2019]BuyFlag[BJDCTF2020]Easy MD5[HCTF 2018]admin第一种方法 直接登录第二种方法 flack session伪造第三种方法Unicode欺骗 [MRCTF2020]你传你🐎呢[护网杯 2018]easy_tornadoSSTI注入 [ZJCTF 2019]NiZhuanSiWei [极客大挑战 2019]BuyFlag 一…...
【ubuntu系统】在虚拟机内安装Ubuntu
Ubuntu系统装机 描述新装机后的常规配置, 虚拟机使用vbox terminal 打不开 CTRL ALT F3 进入命令行模式(需要返回桌面时CTRL ALT F1)root用户登入cd /etc/default vi locale LANG“en_US” 改成 LANG“en_US.UTF-8”保存修改后&…...
Python初学者必须掌握的基础知识点
Python初学者必须掌握的基础知识点包括数据类型与变量、控制结构(条件语句和循环语句)、基本数据结构(列表、元组、字典、集合)、函数与模块、以及字符串处理等。以下是对这些基础知识点及其对应代码的详细介绍: 1. …...
ESP32是什么?
ESP32是一款由乐鑫信息科技(Espressif Systems)推出的高度集成的低功耗系统级芯片(SoC),它结合了双核处理器、无线通信、低功耗特性和丰富的外设,特别适用于各种物联网(IoT)应用。以…...
怎样申请网站/seo如何去做优化
有时候对python中的数组进行操作时,会用到python中的all()与any()函数,这里记录一下: all():当可迭代对象为空时返回True。或者当可迭代对象中是否所有值都为True,所有值都为True,则返回True。否则返回False。 any():当可迭代对象…...
网站建设与维护大作业/googleplay商店
SA:SA指System Analyst(系统分析师)RD:RD指Research and Development(研发)SD:SD指Software Development(软件开发)SE:SE指Software Development Engineer(软件开发工程师)QA:QA指QUALITY ASSURANCE(品质保证)转载于:https://www.…...
上海软件开发企业/seo关键词优化外包
泛音的种类和多少决定了音色 为什么有的人声音听起来暗,有的人听起来亮?事实上即使是唱同一个音高,大家的音色也天差地别,差别就在泛音上了。 一般来说 1. 泛音越充分的声音越饱满。 2. 低频泛音越充分的声音听起来越“厚实”…...
东莞服务行业推广软件/赣州seo外包怎么收费
在三大运营商中,联通与互联网企业的合作最积极,近日中国联通研究院院长张云勇透露腾讯大王卡已经发展了5000多万用户,那么这一成绩对联通全是好事么?拿数据来对比看看。中国联通与互联网企业的合作始自去年10月份,2016…...
多人运动免费正能量网站链接/中国百强城市榜单
2019独角兽企业重金招聘Python工程师标准>>> Hadoop资源共享啦! 上个暑假,将《Hadoop权威指南》认真看了一遍,看的是第二版。给我最深的体味就是这本书将理论讲得很深入全面、并将理论提 升到了实践,比如说࿰…...
前端需要学wordpress/热狗网站排名优化外包
1、安装scala的语言包 https://www.scala-lang.org/download/ 下载安装即可!!! 2、配置Idea 我们可以直接在Idea工具上下载 File——settings——plugins,输入Scala搜索(下图是我已经安装好了) 3、Scal…...