设计模式:代理模式

embedded/2024/9/25 19:15:35/

文章目录

  • 一、什么是代理模式
  • 二、代理模式的结构
    • 1、介绍
    • 2、代码实现样例
      • (1)静态代理
      • (2)动态代理
  • 三、代理模式的应用场景

一、什么是代理模式

为某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。

代理模式的主要优点是:

  • 功能增强:可以在不修改真实主题的情况下,增加额外的功能。
  • 控制访问:可以控制对真实主题的访问,例如,在访问真实主题之前检查权限。
  • 延迟加载:可以延迟加载真实主题,直到真正需要时才加载。

代理模式的主要类型有:

  • 静态代理:静态代理类在程序运行前就已经存在,一般这种代理类是由程序员来创建的。
  • 动态代理:动态代理类的源码是在程序运行时生成的,需要用到 JDK 的一些反射类库。

使用代理模式时,需要注意以下几点:

  • 代理模式使得代码更加灵活,但也增加了复杂性。因此,在不需要额外功能或控制访问的情况下,应避免使用代理模式。
  • 在使用动态代理时,需要注意反射操作可能带来的性能问题。

总的来说,代理模式是一种非常有用的设计模式,它可以在不修改原始对象的情况下,为其添加额外的功能或控制其访问。但是,它也可能增加代码的复杂性,因此需要谨慎使用。

二、代理模式的结构

1、介绍

在这里插入图片描述

  • Subject(抽象角色):通过接口或抽象类声明真实角色实现的业务方法。这是代理模式的基础,它定义了代理和真实对象都应该遵循的公共接口或抽象类。
  • Proxy(代理角色):实现抽象角色,是真实角色的代理。它持有真实角色的实例,并在调用真实角色的业务逻辑方法前后添加一些额外的操作,如日志记录、权限校验、事务管理等。代理角色通过调用真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
  • RealSubject(真实角色):实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。这是实际执行具体业务逻辑的对象。

2、代码实现样例

(1)静态代理

  • 定义一个接口及其实现类:
// 抽象角色(接口)  
public interface Subject {  void request();  
}  // 真实角色  
public class RealSubject implements Subject {  @Override  public void request() {  System.out.println("Called RealSubject request()");  }  
}  // 代理角色  
public class ProxySubject implements Subject {  private RealSubject realSubject;  public ProxySubject(RealSubject realSubject) {  this.realSubject = realSubject;  }  @Override  public void request() {  // 在调用真实对象之前,可以添加一些操作  System.out.println("Before calling RealSubject request()");  realSubject.request();  // 在调用真实对象之后,可以添加一些操作  System.out.println("After calling RealSubject request()");  }  
}
  • 静态代理使用
public class StaticProxyDemo {  public static void main(String[] args) {  RealSubject realSubject = new RealSubject();  ProxySubject proxySubject = new ProxySubject(realSubject);  proxySubject.request(); // 输出代理前后的额外信息和真实角色的信息  }  
}

(2)动态代理

动态代理通常使用java.lang.reflect.Proxy类和java.lang.reflect.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("Called RealSubject request()");  }  
}  // 调用处理器  
public class DynamicProxyHandler implements InvocationHandler {  private Object subject;  public DynamicProxyHandler(Object subject) {  this.subject = subject;  }  @Override  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  // 在调用方法之前可以添加一些操作  System.out.println("Before calling method: " + method.getName());  Object result = method.invoke(subject, args); // 调用真实对象的方法  // 在调用方法之后可以添加一些操作  System.out.println("After calling method: " + method.getName());  return result;  }  
}  // 使用动态代理  
public class DynamicProxyDemo {  public static void main(String[] args) {  RealSubject realSubject = new RealSubject();  InvocationHandler handler = new DynamicProxyHandler(realSubject);  // 创建代理对象  Subject proxySubject = (Subject) Proxy.newProxyInstance(  RealSubject.class.getClassLoader(),  realSubject.getClass().getInterfaces(),  handler  );  // 调用代理对象的方法  proxySubject.request(); // 输出代理前后的额外信息和真实角色的信息  }  
}

在这个动态代理的示例中,DynamicProxyHandler类实现了InvocationHandler接口,并重写了invoke方法。这个invoke方法会在每次调用代理对象的方法时被调用。在invoke方法中,你可以添加在调用真实对象方法前后的额外逻辑。

Proxy.newProxyInstance方法用于动态创建代理对象。它接受三个参数:类加载器、代理对象需要实现的接口数组、以及一个InvocationHandler对象。返回的是实现了指定接口的代理对象。

动态代理比静态代理更加灵活,因为它可以在运行时动态地创建代理对象,而无需为每个真实角色编写特定的代理类。但是,动态代理也有其局限性,比如它只能代理实现了接口的类。如果需要代理没有实现接口的类,则需要使用其他方法,如CGLIB等库。

三、代理模式的应用场景

代理模式的应用场景十分丰富,以下是一些具体的例子:

  • 远程代理:
    当你使用网络应用或在线服务时,客户端通常不会直接访问服务器上的对象,而是通过代理服务器进行中转。代理服务器隐藏了实际的网络通信细节,使得客户端能够更方便、安全地访问远程资源。
  • 虚拟代理:
    在图片加载应用中,对于大量或大尺寸的图片,如果一次性加载所有图片可能会导致内存不足或页面加载缓慢。这时,可以使用虚拟代理,先加载低分辨率或缩略图,当用户需要查看高清图片时,再加载真实的高清图片。
    在视频流媒体服务中,为了节省带宽和提高用户体验,通常不会一次性加载整个视频文件。而是使用虚拟代理,先加载视频的元数据或初始片段,然后按需加载后续的视频内容。
  • 安全代理:
    在企业应用中,为了保护敏感数据,如用户密码或财务数据,可以使用安全代理来限制对这些数据的直接访问。代理会检查用户的权限,并只在用户拥有足够权限时才允许访问。
    在一些金融交易系统中,安全代理可以用来监控和记录所有的交易操作,以防止未经授权的访问和恶意操作。
  • 缓存代理:
    在Web应用中,为了提高页面加载速度,通常会对一些频繁访问的数据进行缓存。这时,可以使用缓存代理来拦截对目标对象的请求,并检查是否已经有缓存的结果。如果有,则直接返回缓存的结果,避免重复计算或访问数据库。
    在分布式系统中,缓存代理还可以用来减少跨网络的通信次数,提高系统的响应速度。
  • 事务代理:
    在数据库操作中,为了保证数据的完整性和一致性,通常会使用事务来包裹一系列的数据库操作。事务代理可以确保这些操作要么全部成功,要么全部失败回滚,从而避免数据的不一致状态。
  • 文件访问代理:
    在文件系统中,为了控制对文件的访问权限,可以使用文件访问代理。代理会检查用户的权限,并根据权限来决定是否允许读取、写入或删除文件。

这些例子只是代理模式应用场景的冰山一角,实际上,代理模式的应用非常广泛,几乎可以在任何需要间接访问或控制对象访问的场景中使用。


http://www.ppmy.cn/embedded/5082.html

相关文章

基于SpringBoot的在线拍卖系统

在线拍卖系统是在Java MySQL开发环境的基础上开发的。Java是一种服务器端脚本语言,易于学习,实用且面向用户。全球超过35%的Java驱动的互联网站点使用Java。MySQL是一个数据库管理系统,因为它的体积小但速度快,成本低…

Python框架django项目

创建一个 Django 项目是一个很好的方式来学习和实践 Python web 开发。以下是创建 Django 项目的基本步骤: 步骤: 安装 Django: 首先,确保你已经安装了 Python。然后,使用 pip 安装 Django: pip install d…

(1)认识人工智能

第一章 认识人工智能 引言 本人目前大三,双非一本的人工智能专业,代码能力不算太差,做过项目,也打了比赛,获了奖,但是走技术路线总会有否定自己的感觉,可能是感觉自己的才能没有在搞技术方面实…

《QT实用小工具·二十八》基于qt开发的各种曲线

1、概述 源码放在文章末尾 该项目实现了各种曲线的绘制,下面是项目的demo演示: 项目部分代码如下: #include "frmsmoothcurve.h" #include "ui_frmsmoothcurve.h" #include "smoothcurve.h" #include "…

uniapp H5项目 获取接口的二进制流转化成图片url(base64)

如果你使用的是uniapp, 并且你从接口获取下来的数据长这样: 想要把取到的数据展示成图片,那么你可以这样做: // 这是我们的项目封装的请求方法const res await this.$api.getKaptcha({originResponse: true, // 这样写是为了在request那边特…

spring webflux 小结

一、WebFlux 简介 WebFlux 是 Spring Framework5.0 中引入的一种新的反应式Web框架。通过Reactor项目实现Reactive Streams规范,完全异步和非阻塞框架。本身不会加快程序执行速度,但在高并发情况下借助异步IO能够以少量而稳定的线程处理更高的吞吐&…

《魔兽世界》本人收集的二十六个单机版游戏,内有视频架设教程,非常详细,云盘下载

《魔兽世界》(World of Warcraft)属于大型多人在线角色扮演游戏。 《魔兽世界》本人收集的二十六个单机版游戏,内有视频架设教程,非常详细 下载地址: 链接:https://pan.baidu.com/s/1zu7lUNupkPnLPVYHM4jL5…

11篇 Es集群环境安装的步骤

搭建Elasticsearch(ES)集群环境是一个涉及多个步骤的过程,需要仔细规划和配置。以下是搭建ES集群的一般步骤和要点: 1. **规划集群规模和角色分配**: - 确定集群的规模,包括节点数量和预期的数据量。 …