当前位置: 首页 > java >正文

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

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

定义

代理模式(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.xdnf.cn/news/10320.html

相关文章:

  • 【位运算】两整数之和(medium)
  • DAY 34 超大力王爱学Python
  • 设计模式——责任链设计模式(行为型)
  • Linux线程同步实战:多线程程序的同步与调度
  • 在 SpringBoot+Tomcat 环境中 线程安全问题的根本原因以及哪些变量会存在线程安全的问题。
  • 代谢组数据分析(二十六):LC-MS/MS代谢组学和脂质组学数据的分析流程
  • 【Linux】shell的条件判断
  • gin 常见中间件配置
  • 系统思考:整体观和心智模式
  • Chrome 通过FTP,HTTP 调用 Everything 浏览和搜索本地文件系统
  • 基于STM32单片机CO气体检测
  • C56-亲自实现字符串拷贝函数
  • python连接邮箱,下载附件,并且定时更新的方案
  • SSM框架前后端网站显示不出来图片
  • stm32——SPI协议
  • 随机响应噪声-极大似然估计
  • 飞腾D2000与FPGA结合的主板
  • C语言基础(08)【循环结构】
  • 吴恩达MCP课程(2):research_server
  • 深入剖析Java类加载机制:双亲委派模型的突破与实战应用
  • 头歌java课程实验(Java面向对象 - 包装类)
  • C++语法系列之右值
  • vedio.ontimeupdate()和video.onloadeddata()
  • C++二叉树常见OJ题分析
  • 2025-05-31 Python深度学习10——模型训练流程
  • 一些常用的命令
  • 1.JS逆向简介
  • JSR 303(即 Bean Validation)是一个通过​​注解在 Java Bean 上定义和执行验证规则​​的规范
  • 704SJBH蓝天影院订票网站的设计
  • 极智项目 | 多模态大模型推理平台-Streamlit版(支持Qwen2.5/InternVL3/KimiVL三大模型)