设计模式之——代理模式

代理模式

前言:

我们一般在租房子时会去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做;再比如我们打官司需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法;再比如在淘宝上面买东西,你使用支付宝平台支付,卖家请物流公司发货,在这个过程汇总支付宝、物流公司都扮演者“第三者”的角色在帮你完成物品的购买,这里的第三者我们可以将其称之为代理者,在我们实际生活中这种代理情况无处不在!

一.什么是代理模式

代理模式的设计动机是通过代理对象来访问真实对象,通过建立一个对象代理类,由代理对象控制原对象的引用,从而实现对真实对象的操作。在代理模式种,代理主要起到了一个中介的作用,用于协调与连接调用者(即客户端)和被调用者(即目标对象),在一定程度上降低了系统的耦合度,同时也保护了目标对象。但缺点是在调用者与被调用者增加了地阿里对象,可能会造成请求的处理速度变慢。

二.分类

代理模式分为三类:1.静态代理;2.动态代理;3.CGLIB代理

三.UML结构图

  • Subject:抽象角色,声明了真实对象和代理对象的共同接口;
  • Proxy:代理角色,实现了与真实对象相同的接口,所以在任何时刻都能够代理真实对象,并且代理对象内部包含了真实对象的引用,所以它可以操作真实对象,同时也可附加其他的操作,相当于对真实对象进行封装。

四.特点

优点:

  • 代理模式可以隐藏真实对象的实现细节,使客户端无需知道真实对象的工作方式和结构。
  • 通过代理类来间接访问真实类,可以在不修改真实类的情况下,对其进行扩展,优化或添加安全措施。
  • 代理模式实现起来简单,易于扩展和维护,符合面向对象设计原则中的开闭原则。

缺点:

  • 代理模式可能会引入额外的复杂性和间接性,增加程序设计和维护的难度。
  • 对象代理可能会降低系统性能,特别是在处理大数据或频繁调用的情况下,因为代理需要额外的计算和网络通信开销。

五.应用场景

  • AOP:通过定义切面,切入点和通知等,Spring AOP在运行时生成代理对象,将切面逻辑织入到目标对象的方法调用中。代理对象在方法调用前后执行附加操作,如日志记录,性能监控等。
  • 动态代理(JDK动态代理,CGLIB代理等):当Bean类实现了接口时,Spring使用JDK动态代理来为Bean生成代理对象;当Bean类没有实现接口时,Spring使用CGLIB代理来生成代理对象。

六.代码实现

1.静态代理:

静态代理是一种在代码编写期进行代理类和被代理类的关联的代理方式。具体实现是创建一个代理类,通常需要实现与被代理类相同的接口或继承被代理类。

房东接口类:Landlord1Service,

注意:静态代理实现它的真实对象只能有一个,多个的话,代理对象不能确定哪个对象需要被代理,会导致报错,JDK动态代理没这个问题

/*** 房东* @author Created by njy on 2023/5/30*/
public interface Landlord1Service {/*** 出租* @param money 金额* @return*/void rent(Integer money);
}

租客:TenantImpl

/*** 租客* @author Created by njy on 2023/5/30*/
@Component
public class TenantImpl implements Landlord1Service {@Overridepublic void rent(Integer money) {System.out.println("租下"+money+"元一个月的房子");}
}

静态代理:ProxyImpl

/*** 中介* @author Created by njy on 2023/5/30*/
@Component
public class ProxyImpl implements Landlord1Service {/*** 房东有很多套房子,不想亲自出马了,于是找来了中介*/@Autowiredprivate Landlord1Service target;/*** 优点就是在不改变原来的实现类的情况下对方法实现了增强* 缺点是如果原来的接口新增了方法,那么这里也要对应实现新的方法* @param money 金额* @return*/@Overridepublic void rent(Integer money) {System.out.println("[静态代理]交中介费");target.rent(money);System.out.println("[静态代理]中介负责维修管理");}
}

测试:

/*** @author Created by njy on 2023/5/30*/
@SpringBootTest
public class TestProxy {@Autowiredprivate TenantImpl tenant;@Autowiredprivate ProxyImpl proxy;//1.静态代理@Testvoid TestStatic(){tenant.rent(1000);System.out.println();proxy.rent(2000);}
}

使用场景:

  • 当代理对象只有一个时,可以使用静态代理。
  • 当被代理的类的接口比较稳定时,可以使用静态代理。
  • 当需要为多个被代理的类提供代理时,会导致代理类过多,不方便管理和维护,所以不建议使用静态代理。

2.JDK动态代理

JDK动态代理是一种比较常见的代理方式,他是在程序运行时动态生成代理类,也就是说我们在编写代码时并不知道具体代理的是什么类,而是在程序运行时动态生成。

房东接口类:Landlord2Service

/*** @author Created by njy on 2023/5/30*/
public interface Landlord2Service {/*** 出租* @param money* @return*/void rent(Integer money);
}

租客1:Teant1Impl

/*** @author Created by njy on 2023/5/30*/
@Component
public class Teant1Impl implements Landlord2Service{@Overridepublic void rent(Integer money) {System.out.println("tenant1租下"+money+"元一个月的房子");}
}

租客2:Teant2Impl

/*** @author Created by njy on 2023/5/30*/
@Component
public class Tenant2Impl implements Landlord2Service {@Overridepublic void rent(Integer money) {System.out.println("tenant2租下"+money+"元一个月的房子");}
}

JDK动态代理:JDKProxy

/*** JDK动态代理:就是把代理抽象了一下* @author Created by njy on 2023/5/30*/
public class JDKProxy {private Object target;public JDKProxy(Object target){this.target=target;}/*** 给目标对象生成代理对象* @return 代理生成的对象*/public Object getProxyInstance(){return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),//这里是要实现jdk代理InvocationHandler的接口,lambda表达式(proxy,method,args)->{//执行对象方法System.out.println("[JDK动态代理]交中介费");method.invoke(target,args);System.out.println("[JDK动态代理]中介负责维修管理");return null;});}
}

Test:

/*** @author Created by njy on 2023/5/30*/
@SpringBootTest
public class TestProxy {@Autowiredprivate Teant1Impl teant1;@Autowiredprivate Tenant2Impl tenant2;//2.JDK动态代理@Testvoid TestJDK(){Landlord2Service proxyInstance1 = (Landlord2Service) new JDKProxy(teant1).getProxyInstance();proxyInstance1.rent(2500);System.out.println();Landlord2Service proxyInstance2 = (Landlord2Service) new JDKProxy(tenant2).getProxyInstance();proxyInstance2.rent(2500);}
}

使用场景:

  • 对象必须实现一个或多个接口。
  • 代理类的代理方法不需要额外的逻辑。

3.Cglib代理

CDLIB代理是在运行时动态生成代理类的方法,它使用的库是cglib,和JDK代理相比,它不是动态的生成一个实现了接口的代理类,而是直接在内存中构建一个被代理的子类,并重写父类的方法来进行代理。

房东类:Landlord3Service

/*** @author Created by njy on 2023/5/30*/
@Component
public class Landlord3Service {/*** 出租房屋* @param money* @return*/public void rent(Integer money){System.out.println("租下"+money+"元一个月的房子");}
}

Cglib代理类:CglibProxy

/*** JDKProxy:cglib子类代理工厂* 1.代理的类不能为final* 2.目标对象的方法如果为final/static,那么就不会被拦截,也不会执行目标对象的业务方法* @author Created by njy on 2023/5/30*/
public class CglibProxy implements MethodInterceptor {/*** 目标对象*/private final Object target;public CglibProxy(Object target){this.target=target;}public Object getProxyInstance(){//1.工具类Enhancer en=new Enhancer();//2.设置父类en.setSuperclass(target.getClass());//3.设置回调函数en.setCallback(this);//4.创建子类(代理对象)return en.create();}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("[Cglib代理]交中介费");method.invoke(target,objects);System.out.println("[Cglib代理]中介负责维修管理");return null;}
}

测试:

/*** @author Created by njy on 2023/5/30*/
@SpringBootTest
@RequiredArgsConstructor
public class TestProxy {@Autowiredprivate Landlord3Service landlord3Service;//3.Cglib代理@Testvoid TestCglib(){Landlord3Service proxyInstance = (Landlord3Service) new CglibProxy(landlord3Service).getProxyInstance();proxyInstance.rent(3000);}
}

使用场景:

  • 被代理的类没有实现接口或者无法实现接口。
  • 代理类的代理方法需要进行额外的逻辑,如事务处理等。

七.总结

  • 对于没有实现接口的类,只能使用CGLIB代理。
  • 对于实现了接口的类,可以使用JDK代理或者CGLIB代理,如果要求比较高的话,建议使用JDK代理。
  • 对于单个代理类的情况,并且被代理类实现了接口,可以使用静态代理。
  • 对于多个被代理类的情况,建议使用JDK代理或者CGLIB代理。

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

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

相关文章

一文搞定WeakHashMap

写在前面 在缓存场景下,由于内存是有限的,不能缓存所有对象,因此就需要一定的删除机制,淘汰掉一些对象。这个时候可能很快就想到了各种Cache数据过期策略,目前也有一些优秀的包提供了功能丰富的Cache,比如Google的Guava Cache,它支持数据定期过期、LRU、LFU等策略,但它…

P2710 数列/P2042 [NOI2005] 维护数列

题意(以 P2710 为例)思路 使用 FHQ-Treap 进行求解,清晰明了。对于 insert,先将要插入的数建成一棵树,然后将这棵树放入 FHQ-Treap 中。 对于 delete,将要删除的树分离出来,然后把剩下的部分合并即可,将删除的树的树根丢到废弃节点的栈中以备以后使用(节约空间,不然 …

扩展分析C语言单双引号、反斜杠与注释

目录注释奇怪的注释C风格的注释无法嵌套一些特殊的注释注释的规则建议反斜杠\反斜杠有续行的作用,但要注意续行后不能添加空格回车也能起到换行的作用,那续行符的意义在哪?反斜杠的转义功能单引号和双引号字面值,字符串,字符,字符变量的大小为什么sizeof(1)的大小是4 ?char…

扩展分析单双引号、反斜杠与注释

目录注释奇怪的注释C风格的注释无法嵌套一些特殊的注释注释的规则建议反斜杠\反斜杠有续行的作用,但要注意续行后不能添加空格回车也能起到换行的作用,那续行符的意义在哪?反斜杠的转义功能单引号和双引号字面值,字符串,字符,字符变量的大小为什么sizeof(1)的大小是4 ?char…

C----函数递归之反汇编

环境 win10 vc6.0 debug 代码 关于求阶层问题:n!=n(n-1)!;(n-1)! = (n-1)(n-2)! 例如5!=5(4)! 4!=43! 3!=32! 2!=21 函数递归的出口是1,所以函数递归最重要的条件是去寻找递归的出口 int fun(int i) {int sum = 0;if (i == 1){return 1;}else{sum = i*fun(i-1);}return sum …

地平线占用预测 FlashOcc 参考算法-V1.0

1.简介 3D Occupancy Networks 的基本思路是将三维空间划分成体素网格,并对每个网格进行各类感知任务的预测。目前以网格为中心的方法能够预测每个网格单元的占用率、语义类别、未来运动位移和实例信息。3D occupancy 可以对道路障碍物进行更细粒度的划分,同时获取更精确的占…

手脱upx

其实已经是大一下刚开始的事情了,补个档 手动脱壳の新年快乐 查壳,有壳,UPXX32dbg打开文件,查看初始断点点击PUSHAD跟进,CTRL+*设置EIP,开始F8步过,寻找ESP寄存器第一次单个变红的地址此时的内存窗口开始步过第一次步过就发现ESP单个变红,右键跟进内存窗口然后在第一个…

使用firemin降低火狐内存占用

这些年一直使用火狐浏览器,之前一直在AMD平台的机器使用,没有遇到过内存占用过大的问题(可能也与平台无关)。现在在Intel CPU的机器上使用,时间一久,内存就占用很大。试过Firefox/内存消耗严重里面的办法,效果不明显。也试过修改about:config里面的一些选项,也没有达到…

代码随想录算法 - 回溯算法1

题目1 77. 组合 给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1: 输入:n = 4, k = 2 输出: [[2,4],[3,4],[2,3],[1,2],[1,3],[1,4], ]示例 2: 输入:n = 1, k = 1 输出:[[1]]提示:1 <= n <= 20 1 <= k…

错误

PID自己搭的时候,要注意积分模块的位置,搞不明白好久了,原来是我把积分模块的位置放错了。直接用增益模块不容易出错。

OSG开发笔记(三十):OSG加载动力学仿真K模型文件以及测试Demo

前言Osg需要打开模型文件,但是遇到显示动力学仿真的K模型文件,.k文件是一种描述材料属性的文件,比如密度、弹性模量等,该模型文件不是常规中间开放格式,无法直接支持,需要自定义解析并且重建三维模型。 Demo实际非常流程,因为视频转gif导致部分看起来不行:   交互流畅…