Java特性之设计模式【代理模式】

一、代理模式

概述

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式

在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口

主要解决

在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层

何时使用

想在访问一个类时做一些控制

优缺点

优点:

  • 职责清晰
  • 高扩展性
  • 智能化

缺点:

  • 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢
  • 实现代理模式需要额外的工作,有些代理模式的实现非常复杂

注意事项

  • 和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口
  • 和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制

1. 各个角色介绍

1.1 抽象主题(Subject)

  • 定义了真实主题和代理主题的共同接口,这样在任何使用真实主题的地方都可以使用代理主题

1.2 真实主题(Real Subject)

  • 实现了抽象主题接口,是代理对象所代表的真实对象。客户端直接访问真实主题,但在某些情况下,可以通过代理主题来间接访问

1.3 代理(Proxy)

  • 实现了抽象主题接口,并持有对真实主题的引用。代理主题通常在真实主题的基础上提供一些额外的功能,例如延迟加载、权限控制、日志记录等

2. UML图

​ 我们将创建一个 ILandlordService 租房接口和实现了 ILandlordService 接口的实体类。AgentProxy 是一个代理类,减少房东 HostServiceImpl 对象加载的内存占用

在这里插入图片描述

3. 具体例子和代码

角色分配

  • ILandlordService:租房接口
    • HostServiceImpl:房东实现类
    • AgentProxy:中介代理

3.1 租房接口及其实现类

  • ILandlordService
package com.vinjcent.prototype.proxy;/*** @author vinjcent* @description 租房接口* @since 2024/3/27 17:52*/
public interface ILandlordService {/*** 出租** @param money 金额*/void rent(Integer money);}
  • HostServiceImpl
package com.vinjcent.prototype.proxy;/*** @author vinjcent* @description 房东实现类(实现)* @since 2024/3/27 17:55*/
public class HostServiceImpl implements ILandlordService {@Overridepublic void rent(Integer money) {System.out.println("房东处理...");System.out.println("出租" + money + "元一个月的房子");}@Overridepublic String toString() {return "HostServiceImpl{}";}
}
  • AgentProxy
package com.vinjcent.prototype.proxy.static_proxy;import com.vinjcent.prototype.proxy.HostServiceImpl;
import com.vinjcent.prototype.proxy.ILandlordService;/*** @author vinjcent* @description 中介代理* @since 2024/3/27 18:04*/
public class AgentProxy implements ILandlordService {/*** 被代理的对象*/private ILandlordService target;@Overridepublic void rent(Integer money) {if (target == null) {target = new HostServiceImpl();}System.out.println("中介处理...");target.rent(money);}
}

3.2 额外拓展(动态代理:JDK动态代理、CGLib动态代理)

3.2.1 JDK动态代理

JDK动态代理的代理类根据目标实现的接口动态生成,不需要自己编写,生成的动态代理类和目标类都实现相同的接口,JDK动态代理的核心是InvocationHandler接口和Proxy类,缺点是目标类必须有实现接口,如果某个目标类没有实现接口,那么这个类就不能用JDK动态代理

  • JDKProxyFactory
package com.vinjcent.prototype.proxy.dynamic_proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** @author vinjcent* @description JDK动态代理* @since 2024/3/27 17:24*/
public class JDKProxyFactory implements InvocationHandler {/*** 需要被代理的对象*/private final Object object;public JDKProxyFactory(Object object) {this.object = object;}@SuppressWarnings("unchecked")public <T> T getProxy() {Object o = Proxy.newProxyInstance(// 当前线程的上下文ClassLoaderThread.currentThread().getContextClassLoader(),// 代理对象实现的接口this.object.getClass().getInterfaces(),// 处理器自身this);return (T) o;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = new Object();// 进行方法匹配,调用对应方法名的方法if ("rent".equals(method.getName())) {System.out.println("JDK动态代理前置增强");result = method.invoke(object, args);System.out.println("JDK动态代理后置增强");}return result;}@Overridepublic String toString() {return "JDKProxyFactory{" +"object=" + object +'}';}
}
3.2.2 CGLib动态代理

在程序运行时动态生成类的字节码,动态创建目标类的子类对象,在子类对象中增强目标类,CGLib是通过继承的方式实现的动态代理,因此如果某个类被标记为final,它是无法使用CGLib做动态代理的,优点在于不需要实现特定的接口,更加灵活

在这里插入图片描述

  • CglibProxyFactory
package com.vinjcent.prototype.proxy.dynamic_proxy;import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/*** @author vinjcent* @description CGLib动态代理* @since 2024/3/27 22:42:16*/
public class CglibProxyFactory implements MethodInterceptor {@SuppressWarnings("unchecked")public <T> T getProxy(Class<T> clazz) {// Enhancer是CGLIB库中用于动态生成子类的主要类.通过创建Enhancer对象并设置需要代理的目标类、拦截器等参数,可以生成一个代理类Enhancer en = new Enhancer();// 设置代理的父类en.setSuperclass(clazz);// 设置方法回调en.setCallback(this);Object o = en.create();// 创建代理实例.通过调用Enhancer对象的create方法,可以生成一个代理对象.代理对象会继承目标类的方法,并且在调用代理对象的方法时会先调用拦截器的intercept方法,再执行目标方法return (T) en.create();}@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {// 在生成代理类时,需要指定拦截器.拦截器是实现代理逻辑的关键,它会在代理类的方法被调用时拦截调用,并执行相应的逻辑Object result = null;System.out.println("CGLIB动态代理前置增强");// 通过调用代理对象的方法,会触发拦截器的intercept方法.在intercept方法中,可以根据需要执行各种逻辑,比如添加日志、性能统计、事务管理等if ("rent".equals(method.getName())) {// 通过继承的方法实现代理,因此这里调用invokeSuperresult = methodProxy.invokeSuper(o, args);}System.out.println("CGLIB动态代理后置增强");return result;}
}

3.3 测试主函数

package com.vinjcent.prototype.proxy;import com.vinjcent.prototype.proxy.dynamic_proxy.CglibProxyFactory;
import com.vinjcent.prototype.proxy.dynamic_proxy.JDKProxyFactory;
import com.vinjcent.prototype.proxy.static_proxy.AgentProxy;/*** @author vinjcent* @description 代理模式* @since 2024/3/27 18:01*/
public class Main {public static void main(String[] args) {System.out.println("静态代理");// 静态代理ILandlordService landlordService = new AgentProxy();System.out.println("第一次代理调用目标类方法");landlordService.rent(20);System.out.println("\n第二次代理调用目标类的方法");landlordService.rent(25);System.out.println("\n");HostServiceImpl hostService = new HostServiceImpl();System.out.println("JDK动态代理");// JDK动态代理ILandlordService jdkProxy = new JDKProxyFactory(hostService).getProxy();jdkProxy.rent(750);System.out.println("\n");System.out.println("CGLib动态代理");// CGLIB动态代理HostServiceImpl cglibProxy = new CglibProxyFactory().getProxy(hostService.getClass());cglibProxy.rent(750);}}
  • 测试结果

在这里插入图片描述

4. 使用场景

  • 远程代理
  • 虚拟代理
  • Copy-on-Write 代理
  • 保护(Protect or Access)代理
  • Cache代理
  • 防火墙(Firewall)代理
  • 同步化(Synchronization)代理
  • 智能引用(Smart Reference)代理

在这里插入图片描述

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

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

相关文章

项目管理-项目资源管理2/2

项目管理&#xff1a;每天进步一点点~ 活到老&#xff0c;学到老 ヾ(◍∇◍)&#xff89;&#xff9e; 何时学习都不晚&#xff0c;加油 资源管理&#xff1a;6个过程“硅谷火箭管控” ①规划资源管理&#xff1a; 写计划 ②估算活动资源&#xff1a;估算团队资源&…

PPP点对点协议

概述 Point-to-Point Protocol&#xff0c;点到点协议&#xff0c;工作于数据链路层&#xff0c;在链路层上传输网络层协议前验证链路的对端&#xff0c;主要用于在全双工的同异步链路上进行点到点的数据传输。 PPP主要是用来通过拨号或专线方式在两个网络节点之间建立连接、…

docker-本地私有仓库、harbor私有仓库部署与管理

一、本地私有仓库&#xff1a; 1、本地私有仓库简介&#xff1a; docker本地仓库&#xff0c;存放镜像&#xff0c;本地的机器上传和下载&#xff0c;pull/push。 使用私有仓库有许多优点&#xff1a; 节省网络带宽&#xff0c;针对于每个镜像不用每个人都去中央仓库上面去下…

SQL查询语句(三)范围查找关键字

在上一篇文章中&#xff0c;我们介绍了SQL语句中&#xff0c;逻辑关键字的作用&#xff0c;并举例演示了如何用逻辑关键字来组合WHERE子句。在文章的末尾我们提到了两个用于范围查找的关键字IN和BETWEEN。这两个关键字都可以与NOT关键字灵活组合&#xff0c;起到对字句结果取反…

Jmeter用jdbc实现对数据库的操作

我们在用Jmeter进行数据库的操作时需要用到配置组件“JDBC Connection Configuration”&#xff0c;通过配置相应的驱动能够让我们通过Jmeter实现对数据库的增删改查&#xff0c;这里我用的mysql数据库一起来看下是怎么实现的吧。 1.驱动包安装 在安装驱动之前我们要先查看当前…

【刷题篇】双指针(二)

文章目录 1、有效三角形的个数2、查找总价格为目标值的两个商品3、三数之和4、四数之和 1、有效三角形的个数 给定一个包含非负整数的数组 nums &#xff0c;返回其中可以组成三角形三条边的三元组个数。 class Solution { public:int triangleNumber(vector<int>& n…

MQTT服务搭建及python使用示例

1、MQTT协议 1.1、MQTT介绍 MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是一种轻量级的、基于发布/订阅模式的通信协议&#xff0c;通常用于物联网设备之间的通讯。它具有低带宽、低功耗和开放性等特点&#xff0c;适合在网络带宽有限或者网络连接不稳定…

联发科天玑AI开发套件亮相:开发智能终端生成式AI应用的全面工具

在今日召开的天玑开发者大会2024&#xff08;MDDC 2024&#xff09;上&#xff0c;联发科向外界展示了其最新力作——天玑AI开发套件。该套件是为合作伙伴提供终端生成式AI应用开发的一站式工具包&#xff0c;意在简化并加速开发过程。 联发科推出的天玑AI开发套件包括四大核心…

去除图片水印软件-inpaint

一、普通使用教程 亲眼看看使用 Inpaint 从照片中删除不需要的元素是多么容易&#xff1a; 1.1加载图片 1.2 选择要纠正的问题区域 1.3 告别不需要的对象并保存 二、功能 1 修复旧照片 老并不总是意味着坏。我们拥有的一些旧照片对我们来说仍然很重要&#xff0c;因为它们仍…

minio上传文件失败如何解决

1. 做了什么操作 通过接口上传excel文件&#xff0c;返回响应值 2. 错误如图 2. 如何解决 根据错误描述定位到了部署minio的地方minio通过docker部署&#xff0c;找到docker - compose发现配置文件中minio有两个端口&#xff0c;一个是用于api的&#xff0c;一个是用于管理界面…

Dell EMC Storage Unity: Remove/Install Memory Module

SP A 一个内存故障 点击system view -> Enclosures->Top查看 再次查看Alert&#xff0c; 确认内存出现问题 进入Service &#xff0c; 将SP A置为service状态 移出SP A &#xff0c;进行内存更换 更换完内存后&#xff0c;将SP A插入设备&#xff0c;并进行线缆连接 进入…

6层板学习笔记2

说明:笔记基于6层全志H3消费电子0.65MM间距BGA 67、多层板的电源建议直接大面积铺铜,不建议走线,铺铜充分满足其载流能力 68、凡亿推荐表层1OZ的铜厚线宽20MIL能承载1A的电流,内层0.5OZ的铜厚线宽为40MIL能承载1A的电流,过孔直径20MIL(0.5MM)能承载1A左右的电流,实际设…

Django项目中的Nginx+uWSGI

Django项目中的NginxuWSGI部署 配合另一篇博客共同饮用Django项目服务器部署&#xff08;2024最新&#xff09; 一&#xff1a;Nginx uWSGI部署框架 用户浏览器向nginx发送请求&#xff0c;nginx判断请求是动态海事静态&#xff0c;如果是静态请求&#xff0c;则直接返回静态…

(五)JSP教程——response对象

response对象主要用于动态响应客户端请求&#xff08;request&#xff09;&#xff0c;然后将JSP处理后的结果返回给客户端浏览器。JSP容器根据客户端的请求建立一个默认的response对象&#xff0c;然后使用response对象动态地创建Web页面、改变HTTP标头、返回服务器端地状态码…

c++ socket基于TCP

linux网络编程基础api socket 地址api&#xff1a;ip地址和端口对&#xff0c;成为 soccket 地址。 socket 基础api&#xff1a; sys/socket.h 中&#xff0c;包括创建、命名、监听 socket &#xff1b;接受连接、发起连接、读写数据、获取地址信息、检测带外标记、读取设置 s…

unity ui 同屏

一共有三个摄像机&#xff0c;上屏&#xff0c;下屏 和 类似照相机的ccamera 类似照相机的ccamera的设置&#xff1a; 下屏摄像机设置&#xff1a; 下屏交互的Canvas设置&#xff1a; 新建一个canvas&#xff0c;下面放上rawimage&#xff1a; 如果下屏不想显示的内容&#xf…

创建和管理数据库

1. 一条数据的存储过程 存储数据是处理数据的第一步.只有正确的把数据存储起来&#xff0c;我们才能进行有效的处理和分析.否则&#xff0c;只能是一团乱麻.在MySQL中&#xff0c;一个完整的数据存储过程一共有四步 : 创建数据库&#xff0c;确认字段&#xff0c;创建数据表&a…

【练习3】

1.将二叉搜索树转为排序的双向链表 (好久没看数据结构&#xff0c;忘完了&#xff0c;学习大佬的代码&#xff09; class Solution { public:Node* prenullptr,*headnullptr; //pre为每次遍历时的前一个节点&#xff0c;head记录头节点Node* treeToDoublyList(Node* root) {if…

漏扫神器Invicti V2024.4.0专业版

前言 Invicti Professional是Invicti Security公司推出的一个产品&#xff0c;它是一种高级的网络安全扫描工具。Invicti Professional旨在帮助组织发现和修复其网络系统中的潜在安全漏洞和弱点。它提供了全面的漏洞扫描功能&#xff0c;包括Web应用程序和网络基础设施的漏洞扫…

xhs 旋转滑块流程分析

声明 本文章中所有内容仅供学习交流&#xff0c;抓包内容、敏感网址、数据接口均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff0c;若有侵权&#xff0c;请联系我立即删除&#xff01; 前言 本文首发于公众号…