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

news2025/2/25 16:35:12

一、代理模式

概述

在代理模式(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 {

    @Override
    public void rent(Integer money) {
        System.out.println("房东处理...");
        System.out.println("出租" + money + "元一个月的房子");
    }

    @Override
    public 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;


    @Override
    public 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(
                // 当前线程的上下文ClassLoader
                Thread.currentThread().getContextClassLoader(),
                // 代理对象实现的接口
                this.object.getClass().getInterfaces(),
                // 处理器自身
                this
        );

        return (T) o;
    }

    @Override
    public 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;

    }

    @Override
    public 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();
    }

    @Override
    public 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())) {
            // 通过继承的方法实现代理,因此这里调用invokeSuper
            result = 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.coloradmin.cn/o/1654219.html

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

相关文章

Apple强大功能:在新款 iPad Pro 和 iPad Air 中释放 M4 芯片潜力

Apple 的最新强大功能&#xff1a;在新款 iPad Pro 和 iPad Air 中释放 M4 芯片的潜力 概述 Apple 推出配备强大 M4 芯片的最新 iPad Pro 和 iPad Air 型号&#xff0c;再次突破创新界限。新一代 iPad 有望彻底改变我们的工作、创造和娱乐方式。凭借无与伦比的处理能力、令人惊…

CTF-reverse,逆向分析,对“左移4或右移4,即(x<<4) | (x >>4)的加密探讨

博主在刷题过程中遇上这样一个有意思的加密&#xff08;如下图&#xff09;&#xff0c;苦苦思索其逆向运算&#xff0c;被硬控了很久&#xff0c;也没搜到什么资料来解释这个问题&#xff08;也许是太简单&#xff1f;&#xff1f;蒟蒻博主怀疑人生……&#xff09; 经过博主不…

【MATLAB源码-第205期】基于matlab的LDPC译码算法仿真,对比BF算法,最小和算法,对数BP和概率BP四种算法。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 LDPC 码简介 LDPC码是一种通过稀疏奇偶校验矩阵定义的线性分组码&#xff0c;1962年由Gallager首次提出。这种码具有高效的解码性能&#xff0c;尤其在接近香农极限的情况下&#xff0c;其性能表现尤为突出。LDPC码的核心特…

Logstash分析MySQL慢查询日志实践

删除匹配到的行&#xff0c;当前行信息不记录到message中

VMware Workstation 虚拟机学习 安装centos7.9

1打开VMware Workstation--文件--新建虚拟机 #选择对应的&#xff0c;本例选择最新的 2开始安装centos #网卡配置 vi /etc/sysconfig/network-scripts/ifcfg-ens33 TYPEEthernet PROXY_METHODnone BROWSER_ONLYno BOOTPROTOstatic DEFROUTEyes IPV4_FAILURE_FATALno IPV6INITy…

Linux:进程通信(二)信号的保存

目录 一、信号的处理是否是立即处理的&#xff1f; 二、信号如何保存 1、阻塞、未决、递达 2、信号集 3、信号集操作函数 4、sigprocmask函数 5、sigpending 函数 上篇文章我们讲解了信号的产生&#xff1a;Linux&#xff1a;进程信号&#xff08;一&#xff09;信号的产…

【C语言】字符函数和字符串函数--超详解

前言&#xff1a; 在编程的过程中&#xff0c;我们经常要处理字符和字符串&#xff0c;为了⽅便操作字符和字符串&#xff0c;C语⾔标准库中提供了 ⼀系列库函数&#xff0c;接下来我们就学习⼀下这些函数。 1. 字符分类函数 C语⾔中有⼀系列的函数是专⻔做字符分类的&#…

实战BACnet/IP标准通信网关在楼宇自动化中的应用

智慧楼宇建设实现不同设备间的互联互通是一项巨大挑战&#xff0c;尤其是在那些历史悠久的建筑中&#xff0c;新旧系统并存的情况尤为普遍。某大型商业综合体就面临着这样的困境&#xff1a;老旧的暖通空调系统采用Modbus RTU协议&#xff0c;而新部署的能源管理系统却要求BACn…

一文搞懂MySQL索引的数据结构

一、引言 在数据库管理系统中&#xff0c;索引是提高查询性能的关键所在。对于MySQL这类关系型数据库来说&#xff0c;索引更是其优化查询不可或缺的一部分。索引能够大大加快数据的检索速度&#xff0c;减少数据库的I/O操作&#xff0c;提高数据库的整体性能。本文将从索引的…

第2章.STM32开发C语言常用知识点

目录 0. 《STM32单片机自学教程》专栏总纲 2.1. STM32嵌入式开发C语言编程的不同 2.2. C语言常用知识点 2.2.1 位操作 2.2.2 define 宏定义 2.2.3 条件编译 2.2.3.1 #ifdef 2.2.3.2 #ifndef 2.2.3.3 #if !defined 2.2.4 extern 变量声明 2.2.5 typedef 类型别名 …

PPP点对点协议

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

Mysql:Before start of result set

解决方法&#xff1a;使用resultSet.getString&#xff08;&#xff09;之前一定要调用resultSet.next() ResultSet resultSet statement1.executeQuery();while (resultSet.next()){String username1 resultSet.getString("username");int id1 resultSet.getInt…

识货小程序逆向

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01;wx a15018601872&#xff0c;x30184483x…

java io模型

目录 BIO 模型模型一&#xff1a;单线程服务器模型二&#xff1a;多线程服务器 NIO 模型模型一&#xff1a;遍历轮训 select/poll模型二&#xff1a;基于事件响应机制的 epoll BIO BIO 模型 模型一&#xff1a;单线程服务器 执行过程&#xff1a;阻塞等待 connection&#xff…

JS-拖拽元素放大缩小

效果左右布局&#xff0c;拖拽后&#xff0c;宽度放大缩小 其实自己写也可以&#xff0c;不过还是发现了两个好用的js库&#xff0c;既然不需要自己写&#xff0c;当然是能偷懒就偷懒 1、resizerjs 官网地址&#xff1a;https://github.com/eknowles/resizerjs <!doctype …

SPSS多元线性回归

&#xff08;要满足&#xff09;模型的假设条件需要对数据进行怎样处理&#xff1f;&#xff1f; 为了使数据满足多元线性回归的条件&#xff0c;通常需要进行以下预处理步骤&#xff1a; 1. 数据清洗&#xff1a;处理缺失值、异常值和重复值&#xff0c;确保数据质量。 2. 特…

Linux基础之git与调试工具gdb

目录 一、git的简单介绍和使用方法 1.1 git的介绍 1.2 git的使用方法 1.2.1 三板斧之git add 1.2.2 三板斧之git commit 1.2.3 三板斧之git push 二、gdb的介绍和一些基本使用方法 2.1 背景介绍 2.2 基本的使用方法 一、git的简单介绍和使用方法 1.1 git的介绍 Git是一…

NSSCTF中的web

目录 [第五空间 2021]WebFTP [LitCTF 2023]PHP是世界上最好的语言&#xff01;&#xff01; [SWPUCTF 2021 新生赛]PseudoProtocols [LitCTF 2023]导弹迷踪 [NISACTF 2022]easyssrf [第五空间 2021]WebFTP 1.进入页面&#xff0c;发现是登录页面&#xff0c;想到 弱口令&…

JAVA学习笔记(第三周)

文章目录 继承概述使用场景继承的特点子类继承的内容成员变量访问特点成员方法访问特点方法的重写构造方法this super 多态多态的表现形式多态的前提成员变量和方法调用instanceof优势弊端 包包名的规则全类名final常量 权限修饰符代码块 继承 概述 继承就是子类继承父类的特征…

【图书推荐】《从零开始大模型开发与微调:基于PyTorch与ChatGLM》

本书目的 本书详解大模型基本理论、算法、程序实现与应用实战&#xff0c;揭示ChatGLM大模型开发与微调技术&#xff0c;紧跟大模型技术发展趋势&#xff0c;利用ChatGLM大模型完成毕业论文和研究课题。 本书案例 基于PyTorch卷积层的MNIST分类实战PyTorch数据处理与模型展示…