【设计模式-3.4】结构型——代理模式

article/2025/8/14 15:17:44

说明:说明:本文介绍结构型设计模式之一的代理模式

定义

代理模式(Proxy Pattern)指为其他对象提供一种代理,以控制对这个对象的访问,属于结构型设计模式。(引自《设计模式就该这样学》P158)

生活中有许多代理模式的应用,比如明星开演唱会、拍电影时,拉赞助、找场地、找投资这些事情,都不会自己亲自去做,都会有自己的经纪人、经纪公司去具体执行,这样经纪公司就控制了对明星的访问;又比如,工地工人,具有扛沙包、搬砖等能力,他们不会直接找工地老板沟通,而是委托给包工头,让包工头去和工地老板商量,包工头控制了对工地工人的访问。

影星与经纪人

以影星与经纪人为例,如下,是一个影星对象,具有属性名字,唱歌、拍电影方法

/*** 影星*/
public class MovieStar {/*** 名字*/private String name;public MovieStar(String name) {this.name = name;}/*** 唱歌** @param singName 歌名* @return 感谢语*/public String sing(String singName) {System.out.println(name + "在唱" + singName);return "谢谢大家!谢谢大家!";}/*** 拍电影** @param movieName 片名*/public void movie(String movieName) {System.out.println(name + "在拍" + movieName);}
}

经纪人代理了影星对象,如下:

/*** 影星经纪人*/
public class StarProxy {/*** 影星对象*/private MovieStar movieStar;/*** 构造器注入* @param name 影星名称*/public StarProxy(String name) {this.movieStar = new MovieStar(name);}/*** 代理唱歌** @param singName 歌名* @return 感谢*/public String sing(String singName) {System.out.println("经纪人在找场地");return movieStar.sing(singName);}/*** 代理拍电影** @param movieName 片名*/public void movie(String movieName) {System.out.println("经纪人在联系剧组");movieStar.movie(movieName);}
}

使用,如下:

public class Client {public static void main(String[] args) {StarProxy bigStar = new StarProxy("大明星");System.out.println(bigStar.sing("忘情水"));System.out.println("----------------------");bigStar.movie("《无间道》");}
}

运行如下,经纪人对象代理了影星对象

在这里插入图片描述

改进

以上代码,可将影星、经纪人对象的方法抽出来成接口,让他们都实现这个接口,可统一方法列表,如下:

(Star,明星接口)

/*** 明星接口*/
public interface Star {/*** 唱歌能力** @param singName 歌名* @return 感谢*/String sing(String singName);/*** 拍戏能力** @param movieName 片名*/void movie(String movieName);
}

(MovieStar,影星对象)

/*** 影星*/
public class MovieStar implements Star {/*** 名字*/private String name;public MovieStar(String name) {this.name = name;}/*** 唱歌** @param singName 歌名* @return*/@Overridepublic String sing(String singName) {System.out.println(name + "在唱" + singName);return "谢谢大家!谢谢大家!";}/*** 拍电影** @param movieName 片名*/@Overridepublic void movie(String movieName) {System.out.println(name + "在拍" + movieName);}
}

(StarProxy,影星代理)

/*** 影星代理*/
public class StarProxy implements Star {/*** 影星对象*/private MovieStar movieStar;/*** 构造器注入* @param name 影星名称*/public StarProxy(String name) {this.movieStar = new MovieStar(name);}/*** 代理唱歌** @param singName 歌名* @return 感谢*/@Overridepublic String sing(String singName) {System.out.println("代理人在找场地");return movieStar.sing(singName);}/*** 代理拍电影** @param movieName 片名*/@Overridepublic void movie(String movieName) {System.out.println("代理人在联系剧组");movieStar.movie(movieName);}
}

动态代理

以上手动实现代理的方式称为静态代理,在JDK中有提供动态代理,可以不用额外创建一个代理人对象,直接实现代理,如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class DynamicProxy {public static void main(String[] args) {// 1.创建影星对象MovieStar ldh = new MovieStar("刘德华");// 2.动态创建代理/*** Proxy.newProxyInstance()参数说明:* 参数一:类加载器,要使用和被代理对象相同的类加载器* 参数二:代理对象和被代理对象实现相同接口* 参数三:增强逻辑。通常使用匿名内部类编写逻辑*/Star proxy = (Star) Proxy.newProxyInstance(ldh.getClass().getClassLoader(),new Class[]{Star.class},new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 针对对象不同方法设置不同的附加操作if ("sing".equals(method.getName())) {System.out.println("代理人在找场地");} else if ("movie".equals(method.getName())) {System.out.println("代理人在联系剧组");}// 接收方法返回值,不用考虑方法无返回值的情况,是一种书写模式Object returnValue = method.invoke(ldh, args);// 返回方法的返回值return returnValue;}});// 3.执行代理人的方法,实际是影星对象在执行System.out.println(proxy.sing("忘情水"));System.out.println("----------------------");proxy.movie("无间道");}
}

可用lambda表达式简化代码,如下:

import java.lang.reflect.Proxy;public class DynamicProxy {public static void main(String[] args) {// 1.创建影星对象MovieStar ldh = new MovieStar("刘德华");// 2.动态创建代理/*** Proxy.newProxyInstance()参数说明:* 参数一:类加载器,要使用和被代理对象相同的类加载器* 参数二:代理对象和被代理对象实现相同接口* 参数三:增强逻辑。通常使用匿名内部类编写逻辑*/Star proxy = (Star) Proxy.newProxyInstance(ldh.getClass().getClassLoader(),new Class[]{Star.class},(proxy1, method, args1) -> {// 针对对象不同方法设置不同的附加操作if ("sing".equals(method.getName())) {System.out.println("代理人在找场地");} else if ("movie".equals(method.getName())) {System.out.println("代理人在联系剧组");}// 接收方法返回值,不用考虑方法无返回值的情况,是一种书写模式Object returnValue = method.invoke(ldh, args1);// 返回方法的返回值return returnValue;});// 3.执行代理人的方法,实际是影星对象在执行System.out.println(proxy.sing("忘情水"));System.out.println("----------------------");proxy.movie("无间道");}
}

执行如下,可见代理实现

在这里插入图片描述


动态代理是基于反射实现的,反射是通过解析对象的字节码(class)文件,反向实例化对象的技术。详细可参考下面这篇文章,了解了反射技术,我们完全可以自己手动实现一个对象的动态代理。

  • 反射技术

使用场景

代理模式的这种思想,我认为在实际开发中很常见。例如,为了避免频繁访问数据库,我们会将之前查询过的数据(例如根据主键查询某个用户的数据)存入到缓存中,下次当我们再次查询时,调用对应的Service方法,可以在查询数据库之前先去缓存中查一遍,缓存中没有,再去查数据库,这样可以减少查询数据库的频率。这就是代理思想的一种体现。


再例如,有一个面试题,如何解决ArrayList线程不安全的问题,其中的两个答案如下:

(1)定义一个MyArrayList类,继承ArrayList,重写其方法,每个方法用synchronized修饰;

(2)定义一个MyArrayList类,类里实例化一个ArrayList,自定义List的增删改查方法,用synchronized修饰,方法里调用ArrayList对应的方法;

以上两个方法,都是手动创建一个“代理对象”,以控制对对象内ArrayList的访问,也是代理思想的体现。


另外,还有强大的AOP,面向接口编程,也是代理的一种应用。

  • AOP技术

  • 使用AOP处理参数

  • 使用AOP记录请求日志实现

总结

本文介绍了结构型设计模式中的代理模式,参考《设计模式就该这样学》、《秒懂设计模式》两书。


http://www.hkcw.cn/article/aYjjqlhxBb.shtml

相关文章

C++: STL简介与string类核心技术解析及其模拟实现

目录: 一.STL二.string类一、创建对象的6种构造方式二、常用接口解析1. 容量操作2. 元素访问3. 修改操作4. 字符串操作 三.string模拟实现一、设计基础:类结构与资源管理二、拷贝控制:深拷贝的三种实现1. 传统深拷贝2. 现代写法(推荐&#xf…

【复杂网络分析】什么是modularity?

在复杂网络研究中,modularity(模块化程度或模块度) 是衡量网络社区结构(即节点分组为紧密连接的社区,而社区间连接稀疏)的重要指标。它由Mark Newman和Michelle Girvan于2004年提出,广泛用于评估…

模型训练相关的问题

与模型训练相关问题 损失函数Cross entropy loss的含义训练数据有脏数据,怎么处理?loss一直不收敛,怎么排查?连续值的特征怎么处理后输入到机器学习模型当中损失函数Cross entropy loss的含义 在深度学习中,可以看作通过概率分布q ( x )(预测概率)表示概率分布p ( x ) …

【项目记录】登录认证(下)

1 过滤器 Filter 刚才通过浏览器的开发者工具,可以看到在后续的请求当中,都会在请求头中携带JWT令牌到服务端,而服务端需要统一拦截所有的请求,从而判断是否携带的有合法的JWT令牌。 那怎么样来统一拦截到所有的请求校验令牌的有…

Portainer安装指南:多节点监控的docker管理面板-家庭云计算专家

背景 Portainer 是一个轻量级且功能强大的容器管理面板,专为 Docker 和 Kubernetes 环境设计。它通过直观的 Web 界面简化了容器的部署、管理和监控,即使是非技术用户也能轻松上手。Portainer 支持多节点管理,允许用户从一个中央控制台管理多…

基于微信小程序的垃圾分类系统

博主介绍:java高级开发,从事互联网行业六年,熟悉各种主流语言,精通java、python、php、爬虫、web开发,已经做了六年的毕业设计程序开发,开发过上千套毕业设计程序,没有什么华丽的语言&#xff0…

【前端面经】百度一面

写在前面&#xff1a;面经只是记录博主遇到的题目。每题的答案在编写文档的时候已经有问过deepseek&#xff0c;它只是一种比较普世的答案&#xff0c;要学得深入还是靠自己 Q&#xff1a; <html><style>.a {background-color: red;width: 200px;height: 100px;}…

智能体觉醒:AI开始自己“动手”了-自主进化开启任务革命时代

1. 智能体&#xff1a;AI从“工具”到“伙伴”的关键跃迁 1.1 什么是智能体&#xff1f; 智能体&#xff08;Agent&#xff09;是AI的“进化版”——它不再局限于生成文字或图像&#xff0c;而是能像人类一样“规划任务”“调用工具”甚至“协同合作”。例如&#xff0c;一个…

STM32软件spi和硬件spi

核心观点 本文主要介绍了SPI通信的两种实现方式&#xff1a;软件SPI和硬件SPI。详细阐述了SPI通信协议的基本概念、硬件电路连接方式、移位示意图、时序基本单元以及四种工作模式。同时&#xff0c;对W25Q64模块进行了详细介绍&#xff0c;包括其硬件电路、框图以及操作注意事…

MongoDB数据库命令

目录 一、数据库操作 二、集合&#xff08;表&#xff09;操作 三、文档&#xff08;记录&#xff09;CRUD 操作 1、插入文档 2、查询文档 3、更新文档 4、删除文档 四、聚合操作 1、单目的聚合操作 2、聚合管道 3、MapReduce编程 五、索引管理操作 六、用户权限管…

当前最新IDEA社区版安装当前最新的tomcat插件:集成SmartTomcat,提升开发效率

当前最新IDEA社区版安装当前最新的tomcat插件&#xff1a;集成SmartTomcat&#xff0c;提升开发效率 【下载地址】当前最新IDEA社区版安装当前最新的tomcat插件 该项目为开发者提供了详细的指南&#xff0c;帮助在IntelliJ IDEA社区版中安装SmartTomcat插件&#xff0c;以便更高…

Docker 实战——部署 Nginx 镜像容器、Tomcat 镜像容器、MySQL 镜像容器

#设置基础镜像 FROM dadoha/centos7.4.1708 #维护该镜像的用户信息 MAINTAINER zhangsan #安装相关依赖包 RUN yum clean all ; yum -y install proc-devel net-tools gcc zlib zlib-devel make openssl-devel wget #下载并解压nginx软件包 RUN wget http://nginx.org/d…

Nginx和Tomcat实现负载均衡群集部署应用

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f427;Linux基础知识(初学)&#xff1a;点击&#xff01; &#x1f427;Linux高级管理专栏&#xff1a;点击&#xff01; &#x1f510;Linux中firewalld防火墙&#xff1a;点击&#xff01; ⏰️创作时间&…

Linux中使用Docker容器构建Tomcat容器完整教程

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f427;Linux基础知识(初学)&#xff1a;点击&#xff01; &#x1f427;Linux高级管理防护和群集专栏&#xff1a;点击&#xff01; &#x1f510;Linux中firewalld防火墙&#xff1a;点击&#xff01; ⏰️创作…

一文读懂Nginx应用之 Keepalived+Nginx+Tomcat实现高可用负载均衡集群

目录 一、概述 二、环境规划 三、Nginx服务、Tomcat服务安装部署 (一)、Nginx服务安装部署 (二)、Tomcat服务安装部署 1、Tomcat01应用服务器部署应用程序 (1)、tomcat_8081服务 (2)、tomcat_8082服务 2、Tomcat02应用服务器部署应用程序 (1)、tomcat_8081服务 (2)、…

从0到1部署Tomcat和添加servlet(IDEA2024最新版详细教程)

本文不仅细化了每一个步骤&#xff0c;实现了从0到1部署Tomcat和添加servlet。还针对IDEA2024版和以前的版本在部署上的区别&#xff0c;做了详细介绍&#xff0c;尤其是add framework support部分。与此同时&#xff0c;针对控制台中文乱码问题&#xff0c;本文也给出了详细解…

Apache Tomcat RCE 稳定复现 保姆级!(CVE-2024-50379)附视频+POC

原文链接 Apache Tomcat 最新RCE 稳定复现分析 保姆级&#xff01;&#xff01;&#xff01;附复现视频POC 前言 最近爆出 Apache Tomcat条件竞争导致的RCE&#xff0c;影响范围当然是巨大的&#xff0c;公司也及时收到了相关情报&#xff0c;于是老大让我复现&#xff0c;以…

Tomcat 的使用(图文教学)

Tomcat 的使用&#xff08;图文教学&#xff09; 前言一、什么是Tomcat&#xff1f;二、Tomcat 服务器和 Servlet 版本的对应关系三、Tomcat 的使用 1、安装2、目录介绍3、如何启动4、Tomcat 的停止5、如何修改 Tomcat 的端口号6、如何部暑 web 工程到 Tomcat 中 6.1 方式一6.…

linux服务器tomcat日志中文出现问号乱码

目录 一、场景二、排查三、原因四、解决 一、场景 tomcat日志的中文出现问号乱码 乱码示例 ??[377995738417729536]????????? ac??????????????message:二、排查 1、使用locale命令查看服务器当前使用的语言包 发现只用的语言包为utf-8&#xff0…

在【IntelliJ IDEA】中配置【Tomcat】【2023版】【中文】【图文详解】

作为一款功能强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;IntelliJ IDEA为Web服务器提供了卓越的支持&#xff0c;从而极大地简化了程序员在Web开发过程中的工作流程。学习Java Web开发实质上就是掌握如何创造动态Web资源&#xff0c;这些资源在完成开发后&…