代理模式的学习与使用

news2024/11/25 14:50:29

1、代理模式的学习

  代理模式是一种结构型设计模式,它允许你提供一个代理对象,该对象可以控制对其他对象的访问。代理模式通过在代理对象和实际对象之间添加一个中间层,使得代理对象可以代表实际对象执行某些操作,从而实现对实际对象的间接访问。
  代理模式的主要目的是在不改变原始对象的情况下,为其提供额外的功能、控制或保护。它可以在不直接访问实际对象的情况下,管理对象的创建、销毁、访问控制等。

  代理模式涉及以下几个角色:

  1. 抽象主题(Subject):定义了代理对象和实际对象的共同接口,这样代理对象就可以通过实现该接口来代表实际对象。
  2. 真实主题(Real Subject):实际对象,它定义了代理对象所代表的真实对象。代理对象通过调用实际对象的方法来提供额外的功能或控制访问。
  3. 代理(Proxy):代理对象,它包含一个对实际对象的引用,并实现了抽象主题定义的接口。代理对象可以在执行实际对象的操作前后进行一些预处理和后处理,以实现附加的功能。

在这里插入图片描述
  代理模式可以分为以下几种类型:

  1. 静态代理:在编译时就已经确定代理对象和实际对象的关系,代理对象和实际对象是一一对应的关系。代理对象需要显式地实现或继承抽象主题接口,并调用实际对象的方法。
  2. 动态代理:在运行时动态生成代理对象,无需显式地实现或继承抽象主题接口。动态代理通常使用 Java 提供的 Proxy 类或第三方库(如CGLIB)来实现。通过动态代理,可以根据需要代理不同的实际对象,并在运行时添加额外的行为。

2、代理模式的使用

  题目:假设你正在开发一个音乐播放器应用程序,你需要实现一个日志记录器,用于记录每次播放音乐的歌曲名称。使用代理模式来实现一个日志记录器,确保每次播放音乐时都能自动记录日志信息。

2.1、静态代理模式

/**
 * @author myf
 * 抽象主题
 */
public interface MusicPlayer {

    /**
     * 播放
     *
     * @param songName
     */
    void play(String songName);
}
/**
 * @Author: myf
 * @CreateTime: 2023-06-01  16:36
 * @Description: AppleMusicPlayer 真实主题 苹果音乐播放器
 */
public class AppleMusicPlayer implements MusicPlayer {

    @Override
    public void play(String songName) {
        System.out.println("苹果音乐播放器开始播放" + songName + "音乐");
    }
}
/**
 * @Author: myf
 * @CreateTime: 2023-06-01  16:37
 * @Description: AppleMusicPlayerProxy 代理 苹果音乐播放器代理类
 */
public class AppleMusicPlayerProxy implements MusicPlayer {
    private static final Logger LOGGER = LoggerFactory.getLogger(AppleMusicPlayerProxy.class);

    private AppleMusicPlayer appleMusicPlayer;

    public AppleMusicPlayerProxy(AppleMusicPlayer appleMusicPlayer) {
        this.appleMusicPlayer = appleMusicPlayer;
    }

    @Override
    public void play(String songName) {
        LOGGER.info("日志记录:苹果音乐播放器即将开始播放");
        appleMusicPlayer.play(songName);
        LOGGER.info("日志记录:苹果音乐播放器结束播放");
    }
}

  结果

public class ProxyClient {

    public static void main(String[] args) {
        AppleMusicPlayerProxy appleMusicPlayerProxy = 
        new AppleMusicPlayerProxy(new AppleMusicPlayer());
        appleMusicPlayerProxy.play("七里香");
    }
}
21:17:06.907 [main] INFO org.myf.designPattern.design.proxy.staticPattern.AppleMusicPlayerProxy - 日志记录:苹果音乐播放器即将开始播放
苹果音乐播放器开始播放七里香音乐
21:17:06.912 [main] INFO org.myf.designPattern.design.proxy.staticPattern.AppleMusicPlayerProxy - 日志记录:苹果音乐播放器结束播放

2.2、jdk动态代理

  JDK 动态代理是在运行时期生成代理类的字节码。动态代理基于接口实现,通过反射和 InvocationHandler 接口来实现。在运行时,通过调用 Proxy.newProxyInstance 方法创建代理对象,传入被代理接口和调用处理器对象。当代理对象的方法被调用时,实际执行的是调用处理器的 invoke 方法,通过反射调用被代理对象的相应方法,并在前后添加额外的逻辑。

/**
 * @Author: myf
 * @CreateTime: 2023-06-01  16:40
 * @Description: AppleMusicPlayerJdkProxy 基于jdk的苹果音乐播放器代理类
 */
public class AppleMusicPlayerJdkProxy implements InvocationHandler {
    private static final Logger LOGGER = 
    LoggerFactory.getLogger(AppleMusicPlayerJdkProxy.class);
    //传入被代理的对象也就是实际对象
    private Object proxy;
    //构造方法引入实际对象
    public AppleMusicPlayerJdkProxy(Object proxy) {
        this.proxy = proxy;
    }

    /**
     *
     * @param proxy jdk动态代理生成的代理对象
     * @param method 我们所要调用的某个方法
     * @param args 所要调用的方法的入参
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        LOGGER.info("日志记录:苹果音乐播放器即将开始播放");
        //传入实际对象,去执行实际对象的method,args是方法入参
        method.invoke(this.proxy, args);
        LOGGER.info("日志记录:苹果音乐播放器结束播放");
        return null;
    }

}
public class ProxyClient {

    public static void main(String[] args) {
        AppleMusicPlayerJdkProxy appleMusicPlayerJdkProxy = new 
        AppleMusicPlayerJdkProxy(new AppleMusicPlayer());
        //ClassLoader对象,定义了由哪个类加载器来对生成的代理对象进行加载,
        //Interface对象的数组,表示的是动态代理对象所要实现的接口
        //InvocationHandler对象,表示的是动态代理对象在调用方法的时候,会关联到哪一个
        //InvocationHandler对象上。
        MusicPlayer musicPlayer = (MusicPlayer) 
        Proxy.newProxyInstance(MusicPlayer.class.getClassLoader(),
                new Class[]{MusicPlayer.class}, appleMusicPlayerJdkProxy);
        musicPlayer.play("暗香");
    }
}
21:21:28.449 [main] INFO org.myf.designPattern.design.proxy.jdkPattern.AppleMusicPlayerJdkProxy - 日志记录:苹果音乐播放器即将开始播放
苹果音乐播放器开始播放暗香音乐
21:21:28.451 [main] INFO org.myf.designPattern.design.proxy.jdkPattern.AppleMusicPlayerJdkProxy - 日志记录:苹果音乐播放器结束播放

2.3、cglib动态代理

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib-nodep</artifactId>
            <version>2.2</version>
        </dependency>
/**
 * @Author: myf
 * @CreateTime: 2023-06-01  17:17
 * @Description: AppleMusicPlayerCglibProxy 基于cglib的苹果音乐播放器代理类
 */
public class AppleMusicPlayerCglibProxy implements MethodInterceptor {
    private static final Logger LOGGER = 
    LoggerFactory.getLogger(AppleMusicPlayerCglibProxy.class);

    private Enhancer enhancer = new Enhancer();

    public Object getProxy(Class clazz) {
        //设置代理类的父类,即被代理的类
        enhancer.setSuperclass(clazz);
        //设置代理类的回调对象,即拦截器。
        enhancer.setCallback(this);
        //创建代理对象
        return enhancer.create();
    }

    /**
     * @param obj    代理对象即生成的代理类的实例
     * @param method 被拦截的目标方法。
     * @param args   目标方法的参数数组。
     * @param proxy  MethodProxy 对象,用于调用原始方法。
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args,
     MethodProxy proxy) throws Throwable {
        LOGGER.info("日志记录:苹果音乐播放器即将开始播放");
        Object result = proxy.invokeSuper(obj, args);
        LOGGER.info("日志记录:苹果音乐播放器结束播放");
        return result;
    }
}
public class ProxyClient {

    public static void main(String[] args) {
        AppleMusicPlayerCglibProxy appleMusicPlayerCglibProxy = new 
        AppleMusicPlayerCglibProxy();
        AppleMusicPlayer cglibAppleMusicPlayer = (AppleMusicPlayer) 
        appleMusicPlayerCglibProxy.getProxy(AppleMusicPlayer.class);
        cglibAppleMusicPlayer.play("花香");
    }
}
21:39:22.930 [main] INFO org.myf.designPattern.design.proxy.cglibPattern.AppleMusicPlayerCglibProxy - 日志记录:苹果音乐播放器即将开始播放
苹果音乐播放器开始播放花香音乐
21:39:22.943 [main] INFO org.myf.designPattern.design.proxy.cglibPattern.AppleMusicPlayerCglibProxy - 日志记录:苹果音乐播放器结束播放

3、总结

  1. jdk动态代理
      JDK 动态代理基于接口实现。它使用 java.lang.reflect.Proxy 类和java.lang.reflect.InvocationHandler 接口来创建代理对象。
      首先,定义一个实现 InvocationHandler 接口的代理类,它负责处理代理对象的方法调用。
      然后,使用 Proxy.newProxyInstance(ClassLoader, Class[], InvocationHandler) 方法创建代理对象,指定类加载器、要代理的接口列表和代理类的实例。
      当调用代理对象的方法时,实际上会调用 InvocationHandler 实现类的 invoke 方法,通过反射调用被代理对象的方法,并可以在方法调用前后执行额外的逻辑。
  2. CGLIB
      CGLIB(Code Generation Library)是一个强大的高性能代码生成库,它能够在运行时生成子类来代理目标类,而不需要接口。
      CGLIB 基于继承实现代理(不能对final修饰的类进行代理),它使用 net.sf.cglib.proxy.Enhancer 类和 net.sf.cglib.proxy.MethodInterceptor 接口来创建代理对象。首先,定义一个实现 MethodInterceptor 接口的代理类,它负责处理代理对象的方法调用。
      然后,创建 Enhancer 对象,并设置被代理类为其父类,设置 MethodInterceptor 实现类为回调对象。
      最后,通过调用 Enhancer 对象的 create 方法来创建代理对象。
      当调用代理对象的方法时,CGLIB 会拦截方法调用,通过调用 MethodInterceptor 实现类的 intercept 方法,并可以在方法调用前后执行额外的逻辑。在 intercept 方法中,使用 MethodProxy 对象调用父类的方法,从而实现代理。

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

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

相关文章

ARM-系统移植(开发环境搭建)

基于STM32MP157单片机 一、安装tftp服务器 首先保证ubuntu连接网络成功 1. 安装步骤 作用&#xff1a;完成ubuntu和开发板之间传输文件 1&#xff09;安装tftp服务器的安装包 sudo apt-get install tftpd-hpa tftp-hpa tftpd-hpa : 服务器端 tftp-hpa : 客户端 2&#x…

Docker Desktop 如何运行容器

第一次使用windows环境下的DockerDesktop记录下使用方法 1、配置镜像源&#xff0c;虽然配置了镜像源&#xff0c;但是在界面你还是搜索不到镜像&#xff0c;应该默认使用的是官方dockerhub的原因&#xff0c;后面可以手动创建避开这个问题。 2、运行系统的windows powershell…

使用yolov5实现图像识别

文章目录 开始之前下载依赖下载数据集标记数据集整理文件新建 yaml 文件开始训练模型选择训练完成使用模型进行识别自定义模型下载数据集下载地址分享问题 开始之前 你应当先克隆这个仓库 git clone https://github.com/ultralytics/yolov5 # clone下载完毕后&#xff0c;进入…

【轻量化网络系列(5)】ShuffleNetV2论文超详细解读(翻译 +学习笔记+代码实现)

前言 今天我们要讲的是ShuffleNetV2&#xff0c;它是由旷视科技团队在 2018 年提出的&#xff0c;原论文发表在ECCV上。在同等复杂度下&#xff0c;ShuffleNetV2比ShuffleNet和MobileNetv2更准确。这篇论文除了提出这个全新的轻量化网络结构以外&#xff0c;还创新性地提出四…

redis第二章-第二课-主从模式和哨兵模式原理

主从模式 1、复制一份redis.conf&#xff0c;命名为redis-6380.conf文件 2、修改该文件&#xff0c;修改端口号&#xff0c;线程文件&#xff0c;日志文件&#xff0c;以及指定文件存放目录 3.在从节点上配置&#xff0c;这是核心配置 从本机6379的redis实例复制数据 replica…

如何准备一场面试

目录 一、心理准备1、内心的答案2、表述的答案 二、简历准备三、知识准备&面试流程1、一面&#xff1a;技术面2、二面&#xff1a;BOSS面3、终面&#xff1a;HR面 四、面试的技巧有哪些1、自信2、真诚3、主动4、复盘 五、补充&#xff1a;其他面试内容1、逻辑思维2、技术开…

圆柱点云展开为平面点云

文章目录 简单计算图示目标&#xff1a;将圆柱点云展开为平面点云用途&#xff08;目前想到的&#xff09;思路&#xff1a;本文展开思路关键点 简单计算图示 目标&#xff1a;将圆柱点云展开为平面点云 注意&#xff1a;工业实际场景中只能获取可见区域的圆柱侧面&#xff0c…

CC2530+ESP8266与手机APP通信

一、案例介绍 下面是一个基于CC2530和ESP8266的项目示例,演示了如何使用CC2530与ESP8266通信以及使用AT指令控制其WiFi模块设置和数据传输。 项目概述: 通过CC2530控制ESP8266将其配置成AP+TCP服务器模式,并通过手机APP连接到TCP服务器并完成数据传输。ESP8266将作为一个热…

基于Nginx部署的LNMP及搭建论坛

文章目录 1.部署LNMPLNMP架构拓扑图1.安装Nginx服务&#x1f60a;2.安装MySQL服务&#x1f923;3.安装配置PHP解析环境&#x1f602; 2.部署Discuz社区论坛Web应用3. 部署WordPress社区论坛Web应用4. fpm参数优化 1.部署LNMP LNMP架构拓扑图 1.安装Nginx服务&#x1f60a; ##…

DPDK相关开源代码汇总

上篇《DPDK相关学习资料汇总》中&#xff0c;我们讲了学习DPDK的相关资料&#xff0c;今天我们来讲一下DPDK相关的开源代码。 1、suricata Suricata引擎能够进行实时入侵检测(IDS)、内联入侵预防(IPS)、网络安全监控(NSM)和离线pcap处理。是一款开源、快速、高度稳定的网络入…

【Python wxPython】零基础也能轻松掌握的学习路线与参考资料

wxPython是一个基于wxWidgets C类库的Python GUI框架&#xff0c;使用它可以轻松创建跨平台的可视化应用程序。此外&#xff0c;wxPython与Python语言天然融合&#xff0c;让Python程序员可以轻松使用Python编写图形界面程序。因此&#xff0c;学习wxPython实际上就是学习如何用…

基本思维方式

经济学定义 人的欲望是无止境的&#xff0c;而资源有限&#xff0c;这是一个矛盾。 经济学是研究个人和社会如何最优利用由自然和前人提供的有限资源的学问。 经济学的范围 Microeconomics 微观经济学 研究各个行业的运作的以及企业和个人的决策行为的经济学分支。Macroeco…

BTC API:如何在比特币网络上创建应用程序?

比特币是一种去中心化的数字货币&#xff0c;可以通过比特币API与比特币网络进行交互。比特币API是一组允许开发人员与比特币网络进行交互的编程接口&#xff0c;可以帮助开发者构建各种比特币应用程序。 比特币API可以用于创建区块浏览器、钱包和比特币支付。其中利用比特币A…

机器视觉_HALCON_编程指南_多线程编程

文章目录 一、前言二、HALCON并行编程2.1 深入可重入性⭐2.2 多线程编程设计问题2.3 多线程算子2.4 示例 三、结语 一、前言 本文是HALCON的 programmers_guide&#xff08;编程指南&#xff09; 中的2.2章节&#xff0c;章节名直译是——用HALCON进行并行编程&#xff0c;实际…

C++入门:命名空间、函数重载、缺省参数

目录 一&#xff1a;命名空间 1.命名空间的意义 2.命名空间的定义 3.如何使用命名空间中成员&#xff1f; 小结 二&#xff1a;C的输入输出 三&#xff1a;函数重载 1.概念 2.实例 3.为什么C能支持函数重载而C不行&#xff1f; 【1】先看一段C语言代码 【2】我们把上…

基于单片机的七彩音乐喷泉设计

目录 一、方案流程及技术规格书设计 二、系统硬件电路设计 三、软件编写及调试 四、系统调试测试与分析 前言 随着时代的进步&#xff0c;人们对生活质量的要求也在不断提升&#xff0c;因此&#xff0c;51单片机七彩音乐喷泉系统应运而生&#xff0c;它不仅可以满足人们对…

I.MX RT1170加密启动详解(3):HAB加密启动原理

上一节使用对镜像进行签名认证&#xff0c;这可以防止镜像被篡改。但我们还是希望Flash中的程序不会被别人看到&#xff0c;所以这就需要加密启动了。 文章目录 1 HAB加密启动流程2 扩展知识&#xff1a; DCP和SNVS 1 HAB加密启动流程 如下图所示就是HAB加密启动的整个流程&a…

蓝桥杯国赛备赛(嵌入式组)

一、数码管&#xff08;拓展板&#xff09;&#xff08;共阴接法&#xff09; 引脚控制 PA3 :RCLK 串型存储时钟输入 &#xff08;上升沿有效&#xff09; PA2 :SCK 串行移位时钟输入&#xff08;上升沿有效&#xff09; PA1 :SER 串型数据输入 发送数据时先发高位&#xff1…

【UnityShader入门精要】【总结记录】【第二章-1】

☀️博客主页&#xff1a;CSDN博客主页 &#x1f4a8;本文由 萌萌的小木屋 原创&#xff0c;首发于 CSDN&#x1f4a2; &#x1f525;学习专栏推荐&#xff1a;面试汇总 ❗️游戏框架专栏推荐&#xff1a;游戏实用框架专栏 ⛅️点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd;&a…

使用 Access SQL 插入、更新和删除表格记录

参考链接 使用 Access SQL 插入、更新和删除表格记录 | Microsoft Learnhttps://learn.microsoft.com/zh-cn/office/vba/access/concepts/structured-query-language/insert-update-and-delete-records-from-a-table-using-access-sql 将记录插入表格 基本上有两种方法可以向…