12、设计模式之代理模式(Proxy)

一、什么是代理模式
代理模式属于结构型设计模式。为其他对象提供一种代理以控制对这个对象的访问。

在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

二、分类

代理模式分为三类:

  1. 静态代理
  2. 动态代理
  3. CGLIB代理

三、特点
优点:

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

代理模式可能会引入额外的复杂性和间接性,增加程序设计和维护的难度。
对象代理可能会降低系统性能,特别是在处理大数据量或频繁调用的情况下,因为代理需要额外的计算和网络通信开销。
四、应用场景
4.1 生活场景
Window是的快捷键。
支票、银行卡。
律师。
4.2 Java场景

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

五、代码实现
5.0 代码结构
在这里插入图片描述
下面就以房东和租客为例,分别介绍一下静态代理、jdk动态代理和cglib代理。

5.1 静态代理
静态代理是一种在代码编写期进行代理类和被代理类的关联的代理方式。

具体实现是创建一个代理类,通常需要实现与被代理类相同的接口或继承被代理类。
在这里插入图片描述
房东接口类:Landlord1Service,

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

/**
 * 房东
 * 
 */
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 {
 
    @Override
    public void rent(Integer money) {
        System.out.println("租下"+money+"元一个月的房子");
    }
}

静态代理:ProxyImpl

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

测试:


@SpringBootTest
public class TestProxy {
    @Autowired
    private TenantImpl tenant;
 
    @Autowired
    private ProxyImpl proxy;
 
    //1.静态代理
    @Test
    void TestStatic(){
        tenant.rent(1000);
        System.out.println();
        proxy.rent(2000);
    }
}

适用场景:

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

5.2 JDK动态代理
JDK动态代理是一种比较常见的代理方式,它是在程序运行时动态生成代理类,也就是说我们在编写代码时并不知道具体代理的是什么类,而是在程序运行时动态生成。
在这里插入图片描述

房东接口类:Landlord2Service

public interface Landlord2Service {
 
    /**
     * 出租
     * @param money
     * @return
     */
    void rent(Integer money);
}

租客1:Teant1Impl

@Component
public class Teant1Impl implements Landlord2Service{
    @Override
    public void rent(Integer money) {
        System.out.println("tenant1租下"+money+"元一个月的房子");
    }
}

租客2:Teant2Impl


@Component
public class Tenant2Impl implements Landlord2Service {
    @Override
    public void rent(Integer money) {
        System.out.println("tenant2租下"+money+"元一个月的房子");
    }
}

JDK动态代理:JDKProxy

/**
 * JDK动态代理:就是把代理抽象了一下
 * 
 */
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:


@SpringBootTest
public class TestProxy {
    @Autowired
    private Teant1Impl teant1;
    @Autowired
    private Tenant2Impl tenant2;
    //2.JDK动态代理
    @Test
    void TestJDK(){
        Landlord2Service proxyInstance1 = (Landlord2Service) new JDKProxy(teant1).getProxyInstance();
        proxyInstance1.rent(2500);
 
        System.out.println();
        Landlord2Service proxyInstance2 = (Landlord2Service) new JDKProxy(tenant2).getProxyInstance();
        proxyInstance2.rent(2500);
    }
}

适用场景:

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

5.3 Cglib代理
CGLIB代理是在运行时动态生成代理类的方式,它使用的库是cglib,和JDK代理相比,它不是动态的生成一个实现了接口的代理类,而是直接在内存中构建一个被代理类的子类,并重写父类的方法来进行代理。
在这里插入图片描述
房东类:Landlord3Service

@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,那么就不会被拦截,也不会执行目标对象的业务方法
 * 
 */
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();
    }
 
    @Override
    public 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;
    }
}

测试:


@SpringBootTest
@RequiredArgsConstructor
public class TestProxy {
    @Autowired
    private Landlord3Service landlord3Service;
    //3.Cglib代理
    @Test
    void TestCglib(){
        Landlord3Service proxyInstance = (Landlord3Service) new CglibProxy(landlord3Service).getProxyInstance();
        proxyInstance.rent(3000);
    }
}

适用场景:

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

java-单列集合-set系列

set集合继承collection,所以API都差不多,我就不多加介绍 直接见图看他们的特点 我们主要讲述的是set系列里的HashSet、LinkedHashSet、TreeSet HashSet HashSet它的底层是哈希表 哈希表由数组集合红黑树组成 特点:增删改查都性能良好 哈希表具体是…

Seata 2.x 系列【8】Spring Cloud 集成客户端

有道无术,术尚可求,有术无道,止于术。 本系列Seata 版本 2.0.0 本系列Spring Boot 版本 3.2.0 本系列Spring Cloud 版本 2023.0.0 源码地址:https://gitee.com/pearl-organization/study-seata-demo 文章目录 1. 前言2. 问题演…

如何解决跨网文件交换行为分散难管控等问题?

跨网文件交换是指在不同的网络环境之间,如内网和外网,安全地传输文件的过程。这通常涉及到网络隔离的场景,比如政府机构、金融机构、大型企业等,它们为了安全和保密的需要,会通过物理隔离、逻辑隔离等方式,…

《向量数据库指南》——Milvus Cloud BYOC:为数据安全而生?

最近,整个硅谷都在关注 OpenAI 和 Anthropic 的动态。先是 Anthropic 发布了 Claude 3,剑指 GPT-4,被媒体认为“打破了 OpenAI 不可战胜的神话”。这也点燃了整个科技圈的热情,纷纷期待 OpenAI 放出 GPT-5 应战。随后(美东时间 3 月 5 日),OpenAI 发布一则官方公告,主题…

算法打卡day14|二叉树篇03|104.二叉树的最大深度、559.n叉树的最大深度、111.二叉树的最小深度、222.完全二叉树的节点个数

算法题 Leetcode 104.二叉树的最大深度 题目链接:104.二叉树的最大深度 大佬视频讲解:二叉树的最大深度视频讲解 个人思路 可以使用层序遍历,因为层序遍历会有一个层数的计算,最后计算到的层数就是最大深度; 解法 迭代法 就是…

基于SWOT的智能手机企业财务战略研究1.62

摘 要 近些年,网络技术日新月异,智能手机深受消费者喜爱,人们通过网络,手机应用,可以极大地方便人们学习,工作等等。由于国家对电信行业的大力支持,中国消费者群体逐步成为最具潜力的手机购买者…

基于单片机的IC 卡门禁系统设计

摘要:针对传统门锁钥匙易丢失、配置不便和忘记携带等问题,提出了一种基于STC89C52 的IC 卡门禁系统设计。该系统以STC89C52 单片机为核心来控制电子锁模块的开关。主要过程是由RFID 模块读取IC卡ID 并通过串口发送至STC89C52 单片机模块,STC89C52 单片机模块可以实现在线对I…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的吸烟检测系统(深度学习+Python代码+PySide6界面+训练数据集)

摘要:本文详细说明了如何利用深度学习开发一个用于监测吸烟行为的系统,并分享了完整的代码实现。该系统采用了先进的YOLOv8算法,同时还使用YOLOv7、YOLOv6、YOLOv5算法,并对它们进行了性能比较,呈现了不同模型的性能指…

VS中配置生成事件

一、为什么需要使用生成事件? 在实际开发过程中,在项目生成DLL后,需要被复制到不同的目录下被引用,很麻烦。 我们可以利用VS中的项目生成事件属性来进行生成后的DLL复制到指定的目录,或者进去其他的操作,比…

C/C++——Tchisla求解器(多线程高性能版本)

前言 之前一篇文章中介绍的使用Python写的Tchisla求解器Python——Tchisla求解器(暴力搜索法)在我实际使用中有比较大的缺陷,首先是太慢了,对于每日一题中四位数的目标数字,往往搜索数个小时都找不完1~9的全部最优解&…

重学SpringBoot3-ErrorMvcAutoConfiguration类

更多SpringBoot3内容请关注我的专栏:《SpringBoot3》 期待您的点赞👍收藏⭐评论✍ 重学SpringBoot3-ErrorMvcAutoConfiguration类 ErrorMvcAutoConfiguration类的作用工作原理定制 ErrorMvcAutoConfiguration示例代码1. 添加自定义错误页面2.自定义错误控…

【小白学机器学习8】统计里的自由度DF=degree of freedom, 以及关于df=n-k, df=n-k-1, df=n-1 等自由度公式

目录 1 自由度 /degree of freedom / df 1.1 物理学的自由度 1.2 数学里的自由度 1.2.1 数学里的自由度 1.2.2 用线性代数来理解自由度(需要补充) 1.2.3 统计里的自由度 1.3 统计学里自由度的定义 2 不同对象的自由度 2.1 纯公式的自由度&#…

汤唯N次被封后,除了人美外,这些也许你没想到!

汤唯N次被封后,除了人美外,这些也许你没想到! 引言:影坛的璀璨明星 #李秘书讲写作#注意到,在光鲜亮丽的电影圈,有一位女演员以其独特的气质和深入人心的演技,成为了众多观众心中的璀璨明星。她…

國内linux服务器解决Ollama安装超时

curl -fsSL https://ollama.com/install.sh | sh 执行一直超时 做如下配置: 修改hosts文件,直接将http://github.com做个ip指向。 sudo vim /etc/hosts 输入密码后,按 i 增加以下配置 # github 注意下面的IP地址和域名之间有一个空格 140…

朱熹凭着理学成为天选之子,读书方法也很实用

唐朝是李姓的天下,推行老子的道家思想。同时,佛教兴旺鼎盛。儒家开始没落,失去主要地位。为了恢复儒家的地位,朱憙极力发展理学。 理学又叫道学。北有孔子,南有朱子。朱憙是理学集大成者,被称为朱子。理学…

STM32的GPIO初始化配置-学习笔记

简介: 由于刚开始没有学懂GPIO的配置原理,导致后面学习其它外设的时候总是产生阻碍,因为其它外设要使用前,大部分都要配置GPIO的初始化,因此这几天重新学习了一遍GPIO的配置,记录如下。 首先我们要知道芯片…

基于支持向量机SVM的点火电流预测

目录 支持向量机SVM的详细原理 SVM的定义 SVM理论 Libsvm工具箱详解 简介 参数说明 易错及常见问题 完整代码和数据下载链接:基于支持向量机SVM的点火电流预测(代码完整,数据齐全)资源-CSDN文库 https://download.csdn.net/download/abc991835105/88947558 SVM应用实例,基…

.Net Core 中间件验签

文章目录 为什么是用中间件而不是筛选器?代码实现技术要点context.Request.EnableBuffering()指针问题 小结 为什么是用中间件而不是筛选器? 为什么要用中间件验签,而不是筛选器去验签? 1、根据上图我们可以看到,中间件在筛选器之…

【Rockchip android7.1 平台rtl8821cs wifi移植调试】

Rockchip 平台rtl8821cs wifi移植调试 问题描述解决方法 郑重声明:本人原创博文,都是实战,均经过实际项目验证出货的 转载请标明出处:攻城狮2015 Platform: Rockchip rk3128 OS:Android 7.1.2 Kernel: 3.10 问题描述 客户需要在现在的板子上调一款RTL882…

前端的数据标记协议

文章目录 数据标记协议是什么数据标记协议的作用常见的数据标记协议Open Graph protocol 开放图谱协议基本元数据协议可选元数据结构化属性 —— 元数据的属性多个相同的元数据标签类型元数据的使用方法全局类型使用自定义类型使用对象类型使用歌曲对象类型视频对象类型文章对象…