【再探】设计模式—代理模式

 代理是指授权代理人在一定范围内代表其向第三方进行处理有关事务。

1 代理模式

需求:1)将业务代码与非业务代码分离,在不改变代码结构的基础上,为其添加新的功能。2)为系统中的某些操作做同一处理,例如进行鉴权、监控、日志、统计等。

1.1 代理模式介绍

增加一个代理对象,在客户端和目标对象之间起到中介作用,去掉客户不能看到的内容和服务,或者添加客户想要的额外服务。

图 代理模式UML

public class StaticProxy {public static void main(String[] args) {Subject request = new SimpleRequest();Subject proxy = new LogProxy(request);proxy.request("applet","123456");}private interface Subject {void request(String source,String token);}private static class LogProxy implements Subject {private final Subject subject;private LogProxy(Subject subject) {this.subject = subject;}@Overridepublic void request(String source, String token) {System.out.println("记录访问日志,source=" + source + ",token=" + token);subject.request(source,token);}}private static class SimpleRequest implements Subject {@Overridepublic void request(String source, String token) {System.out.println("接收请求:" + source + "," + token);}}}

代理模式

侧重控制对目标对象的访问,及扩展不同于目标对象纬度的功能。

装饰模式

侧重于增强目标对象本身的功能,是继承方案的一个代替。

表 代理模式与装饰模式对比

1.1.1 动态代理

静态代理模式,需要为每个目标对象创建一个代理类,会造成系统中类的数量增多,而且当这些代理类提供的功能相同时,将会造成代码的冗余。动态代理是指在运行时动态创建代理对象。

JDK

JDK自带的一种动态代理方法,要求被代理类必须要实现接口。

原理:动态生成实现目标接口的代理类,调用代理方法时,通过反射的形式来调用目标对象方法。

CGLIB

是CGLIB提供的一种动态代理方法。被代理类可为普通类及接口。

原理:动态生成继承目标类的代理类,通过fastclass 的策略来找到目标方法。

与JDK方式对比,其动态生成的类会比较多,但执行效率会比反射更快。

表 Java中两种动态代理

JDK 方式:

public class JdkDynamicProxy {private interface Subject {void request(String source,String token);}private static class RealRequest implements Subject{@Overridepublic void request(String source, String token) {System.out.println("请求网络");}}private static class RequestIntercept implements InvocationHandler {private final Subject subject;private RequestIntercept(Subject subject) {this.subject = subject;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("请求参数:" + Arrays.asList(args));Object result = method.invoke(subject, args);System.out.println("记录访问日志");return result;}}public static void main(String[] args) {Subject realSubject = new RealRequest();Subject subject = (Subject) Proxy.newProxyInstance(JdkDynamicProxy.class.getClassLoader(), new Class[]{Subject.class}, new RequestIntercept(realSubject));subject.request("小程序","1344");}}

CGLIB方式

public class CglibDynamicProxy {private static class RealRequest {public RealRequest() {}public void request(String source, String token) {System.out.println("请求成功,source=" + source + ",token=" + token);}}private static class RequestIntercept implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("拦截请求:" + Arrays.asList(objects));Object result = methodProxy.invokeSuper(o, objects);System.out.println("记录请求日志");return result;}}public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(RealRequest.class);enhancer.setCallback(new RequestIntercept());RealRequest realRequest = (RealRequest) enhancer.create();realRequest.request("小程序","1344");}
}

1.1.2 远程代理

使得客户端可以访问远程机,将网络细节隐藏起来,客户端可完全认为被代理的远程对象是局域的而非远程的。

远程代理对象承担了大部份网络通信工作,并负责对远程业务方法的调用。

RMI,Remote Method Invocation,远程方法调用,是JDK 实现的一种远程代理。

表 RMI 内部实现步骤

客户端通过一个桩(Stub),相当于代理类对象,与远程主机上的业务对象进行通信。而远程主机端有个Skeleton(骨架)对象来负责与Stub对象通信。

远程机部署代码:

public interface RemoteService extends Remote {String service(String requestInfo) throws RemoteException;}public class RemoteServiceImpl extends UnicastRemoteObject implements RemoteService{public RemoteServiceImpl() throws RemoteException{}@Overridepublic String service(String requestInfo) throws RemoteException {System.out.println("请求:" + requestInfo);return "RemoteService提供远程服务:" + requestInfo;}
}public class JNDIPublisher {public static void main(String[] args) throws RemoteException, MalformedURLException {RemoteService remoteService = new RemoteServiceImpl();int port = 8080;String serviceName = "rmi://localhost:" + port + "/remoteService";LocateRegistry.createRegistry(port);Naming.rebind(serviceName,remoteService);}}

客户端代码:

public class RmiLocal {public static void main(String[] args) throws MalformedURLException, NotBoundException, RemoteException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {String serviceName = "rmi://localhost:" + 8080 + "/remoteService";reflectRemote(serviceName);interfaceRemote(serviceName);}// 这种方式仍然需要导入远程服务提供的接口,RemoteServiceprivate static void reflectRemote(String serviceName) throws MalformedURLException, NotBoundException, RemoteException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {Remote lookup = Naming.lookup(serviceName);Method method = lookup.getClass().getMethod("service", String.class);Object result = method.invoke(lookup, "反射方式");System.out.println("反射方式,远程调用结果:" + result);}private static void interfaceRemote(String serviceName) throws MalformedURLException, NotBoundException, RemoteException{RemoteService remoteService = (RemoteService)Naming.lookup(serviceName);String result = remoteService.service("接口方式");System.out.println("接口方式,远程调用结果:" + result);}}

1.1.3 虚拟代理

主要目标是延迟对象的创建或者复杂计算,直到它们实际需要被创建或计算。这可以提高性能,降低资源消耗。

public class VirtualAgent {private interface Image {void display();}private static class RealImage implements Image {private String path;public RealImage(String path) {System.out.println("创建一个图片需要许多资源,耗费时间较长");this.path = path;}@Overridepublic void display() {System.out.println("展示图片:" + path);}}private static class ProxyImage implements Image {private volatile Image image;private String path;public ProxyImage(String path) {System.out.println("虚拟对象,需要资源及时间都很少,用于延迟真实对象的创建与访问");this.path = path;}@Overridepublic void display() {if (image == null) {synchronized (ProxyImage.class) {if (image == null) image = new RealImage(path);}}image.display();}}public static void main(String[] args) {Image[] images = new Image[10];// 在网页加载过程,先加载这些图片,为了防止陷入卡顿,不直接创建图片,而是先创建虚拟对象System.out.println("网页加载中...");for (int i = 0; i < images.length; i++) {images[i] = new ProxyImage(i + ".png");}System.out.println("网页加载完成--------");// 当网页加载完成后,可以依次加载真实图片了for (Image image : images) {image.display();}}}

1.1.4 缓存代理

为某些目标操作的结果提供临时的存储空间,让后续相同的操作及相同的参数可以直接共享这些结果,而不必再执行计算,以此来提高系统性能。

public class CacheProxy {private static class CacheMethodInterceptor implements MethodInterceptor {private final static Map<MethodKey,Object> resultMap = new HashMap<>();@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {MethodKey methodKey = MethodKey.getKey(method, args);Object result = resultMap.get(methodKey);if (result != null) {return result;} else {Object result2 = methodProxy.invokeSuper(o, args);resultMap.put(methodKey,result2);return result2;}}}// 根据method 及 参数来生成keyprivate static class MethodKey {private static final Map<String,MethodKey> methodKeyMap = new HashMap<>();private String code;private MethodKey(String code) { this.code = code;}public static MethodKey getKey(Method method, Object[] objects) {String hasCode = "method:" + method.hashCode();if (objects != null) hasCode += "args:" + Arrays.hashCode(objects);MethodKey methodKey = methodKeyMap.get(hasCode);if (methodKey == null) {synchronized (MethodKey.class) {methodKey = new MethodKey(hasCode);methodKeyMap.put(hasCode,methodKey);}}return methodKey;}@Overridepublic String toString() {return code;}}private static class ComplexCompute {public ComplexCompute() {}long compute(long num) {System.out.println("执行复杂计算,参数" + num);return num * 2345;}}public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(ComplexCompute.class);enhancer.setCallback(new CacheMethodInterceptor());ComplexCompute compute = (ComplexCompute) enhancer.create();System.out.println(compute.compute(23));System.out.println("----------");System.out.println(compute.compute(123));System.out.println("----------");System.out.println(compute.compute(23));}}

1.2 优缺点

优点:

  1. 能动态扩展对象的功能,而不需要修改原有代码,符合开闭原则。
  2. 能对目标对象的方法访问进行控制与保护。
  3. 将耗费系统资源及时间较多的对象延迟到真正需要使用时再创建,能提高系统性能及系统对客户的友好度。
  4. 能调用远程对象方法,而不需要考虑复杂的实现细节。
  5. 能对运行结果进行缓存,提高系统运行效率。

缺点:

  1. 增加了代理对象,可能会造成请求的处理速度变慢。
  2. 增加了类的数量,让系统变得更复杂。

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

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

相关文章

[实例] Unity Shader 逐像素漫反射与半兰伯特光照

漫反射光照是Unity中最基本最简单的光照模型&#xff0c;本篇将会介绍在片元着色器中实现反射效果&#xff0c;并会采用半兰伯特光照技术对其进行改进。 1. 逐顶点光照与逐像素光照 在Unity Shader中&#xff0c;我们可以有两个地方可以用来计算光照&#xff1a;在顶点着色器…

z3-加法器实验

补码器加减法&#xff0c;运算方法简介 我们要知道什么是补码的加法&#xff0c;我们为什么要用补码的加法&#xff1f; 补码的加法其实就是将两个补码形式的二进制数字直接相加&#xff0c;处理的时候忽略超出固定位数的进位。补码的加法运算和无符号二进制数的加法操作一样&…

【最新区块链论文录用资讯】CCF A — SP 2024 共17篇

Conference&#xff1a;45th IEEE Symposium onSecurity and Privacy CCF level&#xff1a;CCF A Categories&#xff1a;网络与信息安全 Year&#xff1a;2024 Num&#xff1a;17 Efficient Zero-Knowledge Arguments For Paillier Cryptosystem Paillier 加密系统的有效…

基于python的网页自动刷新工具

1.下载webdriver https://msedgewebdriverstorage.z22.web.core.windows.net/?prefix122.0.2365.59/下载Edge的浏览器驱动 2.安装selenium pip install selenium4.11.1 3.写代码 # -*- coding: utf-8 -*- import tkinter as tk from tkinter import messagebox import thr…

【halcon】set_part 实现平移和缩放 彻悟版

背景 之前写了一篇关于set_part 的文章 &#xff0c;确实也实现了平移和缩放。平移是对的&#xff0c;但是缩放其实有畸变。这个问题一直都困扰着我&#xff0c;知道昨天连续测试了好几个小时&#xff0c;直到晚上11点终于完美解决。 坐标和高宽 坐标 再讲set_part 之前&am…

免费撸gpt-4o和各种大模型实用经验分享

项目 Github: https://github.com/MartialBE/one-api 先贴两张图&#xff1a; 说明 免费撸AI大模型,各位可以对照下面我给出的大模型记录表来填&#xff0c;key需要自己去拿&#xff0c;国内都需要手机号验证&#xff0c;如果你不介意。另外我在自己的博客放出免费API给大家…

模型评价指标笔记:混淆矩阵+F1+PR曲线+mAP

评价指标 二分类评价指标 混淆矩阵 TP: 正确预测为了正样本&#xff0c;原来也是正样本 FN: 错误的预测为负样本&#xff0c;原来是正样本 (漏报&#xff0c;没有找到正确匹配的数目) FP: 错误的预测为正样本&#xff0c;原来是负样本 (误报&#xff0c;没有的匹配不正确) TN…

CIM模型

CIM 是 Esri 制图信息模型。 它是一个地图内容规范,用于记录在保存、读取、引用或打开时如何永久保留描述不同项目组件的信息。 该规范以 JSON 表示,适用于 ArcGIS 应用程序和 API 中的地图、场景、布局、图层、符号和样式。 CIM 不仅限于制图设置。 要了解属性的组织方式以及…

【Tools】SpringBoot工程中,对于时间属性从后端返回到前端的格式问题

Catalog 时间属性格式问题一、需求二、怎么使用 时间属性格式问题 一、需求 对于表中时间字段&#xff0c;后端创建对应的实体类的时间属性需要设定格式&#xff08;默认的格式不方便阅读&#xff09;&#xff0c;再返回给前端。 二、怎么使用 导入jackson相关的坐标&#x…

Vue.js - Vue 的安装 以及 常用的 Vue 指令 【0基础向 Vue 基础学习】

文章目录 Vue 快速上手1、Vue.js 官网 & Vue.js 的获取2、创建 Vue 实例&#xff0c;初始化渲染3、插值表达式 安装 Vue 开发者工具&#xff1a;装插件调试 Vue 应用Vue 指令1、v-show 指令2、v-if3、v-else & v-else-if4、v-onv-on 调用传参 5、v-bindv-bind 对于样式…

【算法】前缀和算法——和为k的子数组之和

题解&#xff1a;和为k的子数组之和(前缀和算法) 目录 1.题目2.题解思路2.1前缀和 哈希表&#xff0c;算法步骤&#xff1a;2.2细节如下&#xff1a;2.3参考代码&#xff1a; 3.总结及思考 1.题目 题目链接&#xff1a;LINK 2.题解思路 暴力求解自然不用多说&#xff0c;时…

【SQL】外连接 LEFT JOIN

目录 一.内连接与外连接 1.内连接&#xff08;inner join&#xff09; 2.外连接&#xff08;outer join&#xff09; 二.两表连接 1.我们先来试试看内连接&#xff1a; 2.我们再来试试外连接 三.单表外连接 四.总结 一.内连接与外连接 先得介绍内连接和外连接两个概念&…

第199题|关于函数的周期性问题|函数强化训练(六)|武忠祥老师每日一题 5月24日

解题思路&#xff1a;解这道题我们要用到下面这个结论 f(x)连续&#xff0c;以T为周期时&#xff0c;原函数以T为周期的充分必要条件是&#xff1a; (A) sin x显然是以π为周期的&#xff0c;我们可以看到并不等于0,根据结论&#xff0c;A的原函数显然不是周期函数。 (B) 的…

你以为的私域是真正的私域嘛??你的私域流量真的属于你嘛?

大家好 我是一个软件开发公司的产品经理 专注私域电商行业7年有余 您的私域流量是真正的属于你自己嘛&#xff1f; 私域的定义 私域的界定&#xff1a;一个互联网私有数据&#xff08;资产&#xff09;积蓄的载体。这个载体的数据权益私有&#xff0c;且具备用户规则制定权…

Mysql 备份恢复 mysqldump与xtrabackup备份

1.1 备份的原因 备份是数据安全的最后一道防线&#xff0c;对于任何数据丢失的场景&#xff0c;备份虽然不一定能恢复百分之百的数据 (取决于备份周期)&#xff0c;但至少能将损失降到最低。衡量备份恢复有两个重要的指标&#xff1a;恢复点目标(RPO) 和恢复时间目标(RTO)&…

数据库常用命令(1)

DML 1.添加数据&#xff08;insert into&#xff09; insert into 表名 values (值1&#xff0c;值2....); 表示成功运行&#xff1a; 2.修改数据&#xff08;update&#xff09; update 表名 set 字段名1值1&#xff0c;字段名2值2.....【where条件】 3.删除数据&#xff0…

元年科技数据智能研发部负责人张亚东受邀为第十三届中国PMO大会演讲嘉宾

全国PMO专业人士年度盛会 北京元年科技股份有限公司数据智能研发部负责人张亚东先生受邀为PMO评论主办的2024第十三届中国PMO大会演讲嘉宾&#xff0c;演讲议题为“大模型时代&#xff0c;AI创新型工具提升项目管理效率”。大会将于6月29-30日在北京举办&#xff0c;敬请关注&a…

jmeter之HTTP请求和查看结果树

一、HTTP请求作用&#xff1a; 可以发送post或get请求等请求可以向服务器发送参数或消息体数据可以进行文件上传 HTTP请求&#xff1a;是线程组内的取样器最常用的的一个原件 二、查看界面 添加一个HTTP请求&#xff1a;选择线程组–添加–取样器–HTTP请求 默认界面 名称和…

ThreadLocal为什么会导致内存泄漏?

问题引出&#xff1a; ThreadLocal是为了解决什么问题而产生的&#xff1f; ThreadLocal发生内存泄漏的根本原因是什么&#xff1f; 如何避免内存泄漏的发生&#xff1f;定义 为了解决多个线程同时操作程序中的同一个变量而导致的数据不一致性的问题。   假设现在有两个线程A…

如何获取一个城市或者一个区域的玫瑰风向图?

玫瑰风向图是一种直观展示风向和风速的图形工具&#xff0c;它在气象学、城市规划、农业等领域都有广泛的应用。那么&#xff0c;如何获取某个城市或某个区域的玫瑰风向图呢&#xff1f; 首先&#xff0c;我们可以借助互联网资源获取玫瑰风向图。现代网络技术发达&#xff0c;…