【Java】设计模式——代理模式

news2025/1/12 7:56:09

引言

代理模式(Proxy Pattern)是结构型设计模式之一,旨在为其他对象提供一种代理以控制对该对象的访问。它通过为对象创建一个替代对象或代表对象,在客户端与目标对象之间插入一个中介层,从而达到某些功能,比如延迟加载、权限控制、日志记录等。

Java 中的代理模式分为两种:静态代理和动态代理。在本文中,我们将详细讨论代理模式的概念、实现方式及应用场景。


1. 代理模式概述

代理模式是指通过代理对象间接访问目标对象。代理对象在客户端和目标对象之间充当中介角色,可以对目标对象进行一些额外的处理,比如日志记录、安全控制、事务管理、懒加载等。

1.1 代理模式的组成部分

  • 主题(Subject):目标对象或者接口,通常是被代理的对象。
  • 代理对象(Proxy):负责控制对主题对象的访问,代理对象通常会调用目标对象的相应方法,可以在调用前后加入一些额外的逻辑。
  • 客户端(Client):请求代理对象而不是直接请求主题对象。

2. 代理模式的分类

2.1 静态代理

静态代理是在编译时就确定代理类和目标类的关系,代理类和目标类都实现相同的接口,代理类通过持有目标类的实例来实现方法的转发。

静态代理的优点:

  • 对目标对象的调用透明,不需要修改目标对象的代码。
  • 可以在代理类中添加一些额外的功能,比如日志、安全控制等。

静态代理的缺点:

  • 每增加一个目标类,就需要为其创建一个代理类,代理类的数量会随着目标类的增加而增多,导致代码冗余。

静态代理示例:

// 目标接口
public interface Subject {
    void request();
}

// 目标对象
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: Handling request");
    }
}

// 代理类
public class Proxy implements Subject {
    private RealSubject realSubject;

    public Proxy(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void request() {
        // 在代理类中添加一些额外的功能
        System.out.println("Proxy: Logging before request");
        realSubject.request();
        System.out.println("Proxy: Logging after request");
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Proxy proxy = new Proxy(realSubject);
        proxy.request();
    }
}

输出:

Proxy: Logging before request
RealSubject: Handling request
Proxy: Logging after request

2.2 动态代理

动态代理是在运行时生成代理对象,而不是在编译时生成。这通常使用 Java 的反射机制,通过 Proxy 类动态生成代理对象。

Java 的 java.lang.reflect.Proxy 类和 InvocationHandler 接口提供了动态代理的实现。动态代理适用于接口类型的目标对象,而不需要为每个目标对象创建代理类,具有更好的灵活性。

动态代理的优点:

  • 不需要为每个目标类编写代理类,减少了代码冗余。
  • 可以在运行时动态生成代理对象,灵活性较高。

动态代理的缺点:

  • 需要依赖反射机制,性能稍差。
  • 仅支持接口类型的目标对象。

动态代理示例:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 目标接口
public interface Subject {
    void request();
}

// 目标对象
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: Handling request");
    }
}

// 动态代理处理器
public class DynamicProxyHandler implements InvocationHandler {
    private Object target;

    public DynamicProxyHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Proxy: Logging before request");
        Object result = method.invoke(target, args);
        System.out.println("Proxy: Logging after request");
        return result;
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        
        // 创建动态代理对象
        Subject proxy = (Subject) Proxy.newProxyInstance(
                realSubject.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(),
                new DynamicProxyHandler(realSubject)
        );
        
        proxy.request();
    }
}

输出:

Proxy: Logging before request
RealSubject: Handling request
Proxy: Logging after request

3.代理模式的应用场景

代理模式广泛应用于以下场景:

1.远程代理:当客户端需要访问远程服务器上的对象时,可以使用代理模式来隐藏远程对象的复杂性。例如,RMI(远程方法调用)机制中就是通过代理模式来实现远程方法调用的。

2.虚拟代理:用于延迟加载对象的创建。例如,某些大型对象的加载可能非常昂贵,因此可以通过虚拟代理来推迟对象的创建,直到实际需要时再进行加载。

3.保护代理:用于控制对某些对象的访问,通常用于实现权限控制。例如,在多用户系统中,可以通过保护代理来确保不同角色的用户访问不同级别的数据。

4.缓存代理:通过代理来缓存目标对象的计算结果,避免重复计算。例如,在计算密集型的操作中,可以通过缓存代理来提高性能。

5.智能代理:除了控制访问外,智能代理还可以在调用目标对象方法时执行一些额外的操作,如统计调用次数、管理资源等。


4. 代理模式的优缺点

优点:

  • 透明性:代理对象和真实对象通常实现相同的接口,因此客户端无需了解代理对象的存在。
  • 附加功能:在代理对象中可以添加额外的功能,如权限检查、日志记录等。
  • 降低耦合度:代理模式使得客户端和目标对象之间的关系变得更加松耦合,方便扩展和维护。

缺点:

  • 性能开销:代理模式引入了额外的代理对象,会增加方法调用的时间消耗,尤其是使用动态代理时,反射会带来一定的性能开销。
  • 代码复杂度:在某些场景下,代理模式可能导致系统中出现大量的代理类,增加了代码的复杂性。

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

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

相关文章

一键部署Netdata系统无需公网IP轻松实现本地服务器的可视化监控

文章目录 前言1.关于Netdata2.本地部署Netdata3.使用Netdata4.cpolar内网穿透工具安装5.创建远程连接公网地址6.固定Netdata公网地址 💡 推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。…

HBuilderX(uniapp)实现微信小程序获取用户头像、昵称、授权登录、获取用户手机号

前言:微信文档写的零零散散的,网上搜的教程,23年的教程还在教22年改版之前的东西,导致踩坑无数,所以自己写一下文档记录一下,帮助后来者,记录于2024.11.14 一.获取用户头像和昵称 首先阅读微信…

RabbitMQ基础(简单易懂)

什么是RabbitMQ? 它基于AMQP协议(Advanced Message Queuing Protocol),一种为应用构建消息队列的标准协议。过程中,它提供了一些重要模块:为消息发送的Producer(生产者)&#xff0c…

【web靶场】之upload-labs专项训练(基于BUUCTF平台)

前言 该靶场,是通过平台BUUCTF在线评测中的靶场进行的,基于linux搭建的 当然若是想要该靶场,可以采用github上的醒目,点击后面文字即可访问c0ny1/upload-labs: 一个想帮你总结所有类型的上传漏洞的靶场 或者本人分享在网盘中&a…

美摄科技为企业打造专属PC端视频编辑私有化部署方案

美摄科技,作为视频编辑技术的先行者,凭借其在多媒体处理领域的深厚积累,为企业量身打造了PC端视频编辑私有化部署解决方案,旨在帮助企业构建高效、安全、定制化的视频创作平台,赋能企业内容创新,提升品牌影…

Vue3(elementPlus) el-table替换/隐藏行箭头,点击整行展开

element文档链接: https://element-plus.org/zh-CN/component/form.html 一、el-table表格行展开关闭箭头替换成加减号 注:Vue3在样式中修改箭头图标无效,可能我设置不对,欢迎各位来交流指导 转变思路:隐藏箭头&…

【C++入门】详解(中)

目录 💕1.函数的重载 💕2.引用的定义 💕3.引用的一些常见问题 💕4.引用——权限的放大/缩小/平移 💕5. 不存在的空引用 💕6.引用作为函数参数的速度之快(代码体现) &#x1f4…

【题解】—— LeetCode一周小结53

🌟欢迎来到 我的博客 —— 探索技术的无限可能! 🌟博客的简介(文章目录) 【题解】—— 每日一道题目栏 上接:【题解】—— LeetCode一周小结52 30.二叉树中的链表 题目链接:1367. 二叉树中的链…

Vue方法、计算机属性及侦听器

数组变化侦测 假设我们写了一个数组&#xff0c;现在想让该数组中新增一条数据,那么如何去实现呢&#xff1f; <template><h3>数组变化侦听</h3><button click"addListHandler">添加数据</button><ul><li v-for"(item…

TIOBE编程语言排行靠前的编程语言的吉祥物

Python的吉祥物&#xff1a;小蟒蛇 Python语言的吉祥物是一只名叫"Pythonidae"&#xff08;或简称"Py"&#xff09;的小蟒蛇。这个吉祥物由Tobias Kohn设计于2005年&#xff0c;它的形象借鉴了真实的蟒蛇&#xff0c;但加入了一些可爱和友善的特点。小蟒蛇…

Linux (CentOS) 安装 Docker 和 Docker Compose

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall ︱vue3-element-admin︱youlai-boot︱vue-uniapp-template &#x1f33a; 仓库主页&#xff1a; GitCode︱ Gitee ︱ Github &#x1f496; 欢迎点赞 &#x1f44d; 收藏 ⭐评论 …

Unity热更新 之 Addressables(2) 本地/远端打包 流程测试

基础篇&#xff1a;Unity热更新 之 Addressables(1) 资源基础加载-CSDN博客 基础方法来源于唐老狮,我也是初学热更这一块&#xff0c;所有不保证步骤完全正确&#xff0c;如有不足还请斧正 目录 0.前提 1.本地打包 1.1.资源放入包 1.2.简化路径名称给出标签(如有需要的话) …

Openstack持久存储-Swift,Cinder,Manila三者之间的区别

总结不易&#xff0c;给个三连吧&#xff01;&#xff01;&#xff01; 补充&#xff1a; 文件共享存储服务Manila 在OpenStack生态系统中&#xff0c;Cinder和Manila分别提供了两种不同类型的存储服务&#xff0c;类似于传统的SAN&#xff08;存储区域网络&#xff09;和NAS&…

【TI毫米波雷达】DCA1000不使用mmWave Studio的数据采集方法,以及自动化实时数据采集

【TI毫米波雷达】DCA1000不使用mmWave Studio的数据采集方法&#xff0c;以及自动化实时数据采集 mmWave Studio提供的功能完全够用了 不用去纠结用DCA1000低延迟、无GUI传数据 速度最快又保证算力无非就是就是Linux板自己写驱动做串口和UDP 做雷达产品应用也不会采用DCA1000的…

JavaEE之线程池

前面我们了解了多个任务可以通过创建多个线程去处理&#xff0c;达到节约时间的效果&#xff0c;但是每一次的线程创建和销毁也是会消耗计算机资源的&#xff0c;那么我们是否可以将线程进阶一下&#xff0c;让消耗计算机的资源尽可能缩小呢&#xff1f;线程池可以达到此效果&a…

J-LangChain - 复杂智能链流式执行

系列文章索引 J-LangChain 入门 介绍 j-langchain是一个Java版的LangChain开发框架&#xff0c;具有灵活编排和流式执行能力&#xff0c;旨在简化和加速各类大模型应用在Java平台的落地开发。它提供了一组实用的工具和类&#xff0c;使得开发人员能够更轻松地构建类似于LangC…

《HeadFirst设计模式》笔记(上)

设计模式的目录&#xff1a; 1 设计模式介绍 要不断去学习如何利用其它开发人员的智慧与经验。学习前人的正统思想。 我们认为《Head First》的读者是一位学习者。 一些Head First的学习原则&#xff1a; 使其可视化将文字放在相关图形内部或附近&#xff0c;而不是放在底部…

springboot整合h2

在 Spring Boot 中整合 H2 数据库非常简单。H2 是一个轻量级的嵌入式数据库&#xff0c;非常适合开发和测试环境。以下是整合 H2 数据库的步骤&#xff1a; 1. 添加依赖 首先&#xff0c;在你的 pom.xml 文件中添加 H2 数据库的依赖&#xff1a; <dependency><grou…

安装rocketmq dashboard

1、访问如下地址&#xff1a; GitHub - apache/rocketmq-dashboard: The state-of-the-art Dashboard of Apache RoccketMQ provides excellent monitoring capability. Various graphs and statistics of events, performance and system information of clients and applica…

mysql中创建计算字段

目录 1、计算字段 2、拼接字段 3、去除空格和使用别名 &#xff08;1&#xff09;去除空格 &#xff08;2&#xff09;使用别名&#xff1a;AS 4、执行算术计算 5、小结 1、计算字段 存储在数据库表中的数据一般不是应用程序所需要的格式&#xff0c;下面举几个例子。 …