【设计模式】【结构型模式】代理模式(Proxy)

server/2025/2/22 6:08:48/

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中… 博客更新速度++
👍 欢迎点赞、收藏、关注,跟上我的更新节奏
🎵 当你的天空突然下了大雨,那是我在为你炸乌云

文章目录

一、入门

什么是代理模式

代理模式(Proxy Pattern)是一种结构型设计模式,允许你提供一个代理对象来控制对另一个对象的访问。
代理对象在客户端和目标对象之间起到中介作用,可以在不改变目标对象的情况下增加额外的功能或控制访问。

为什么要代理模式

  1. 违反单一职责原则
    • 原始对象的核心职责是实现业务逻辑,但如果将额外的功能(如权限检查、日志记录等)直接写入原始对象,会导致对象的职责变得复杂,难以维护。
    • 例如,一个UserService类如果既要处理用户登录逻辑,又要记录日志、检查权限,代码会变得臃肿。
  2. 代码重复
    • 如果多个地方需要对对象的访问进行相同的控制(如权限检查),开发者可能会在每个调用点重复编写相同的代码,导致代码冗余。
  3. 难以扩展
    • 如果需要在访问对象时增加新的功能(如缓存、延迟加载等),可能需要直接修改原始对象的代码,这会破坏开闭原则(对扩展开放,对修改关闭)。
  4. 性能问题
    • 某些场景下,对象的创建或初始化成本较高(如加载大文件、连接远程服务等)。如果没有代理模式,可能会在不需要时提前创建对象,导致资源浪费。
  5. 安全性问题
    • 如果没有代理模式,客户端可以直接访问敏感对象,可能会绕过必要的安全检查或权限控制。

如何实现代理模式

  1. Subject(抽象主题):定义真实对象和代理对象的共同接口,客户端通过该接口与真实对象交互。
  2. RealSubject(真实主题):实现Subject接口,是代理对象所代表的真实对象。
  3. Proxy(代理):实现Subject接口,持有对RealSubject的引用,控制对RealSubject的访问,并可以在访问前后添加额外操作。

【案例】订单加强
网购订单处理:假设我们需要在用户下单时记录日志,但不想修改核心的订单处理逻辑。

静态代理

Subject(抽象主题): OrderService接口,定义订单下达。

java">public interface OrderService {void createOrder(String product);
}

RealSubject(真实主题)OrderServiceImpl类,实现下单逻辑。

java">public class OrderServiceImpl implements OrderService {@Overridepublic void createOrder(String product) {System.out.println("订单创建成功,商品:" + product);}
}

Proxy(代理)OrderServiceStaticProxy(手动编写代理类,添加日志记录)

java">public class OrderServiceStaticProxy implements OrderService {private OrderService orderService;public OrderServiceStaticProxy(OrderService orderService) {this.orderService = orderService;}@Overridepublic void createOrder(String product) {System.out.println("[静态代理] 记录日志:开始下单");orderService.createOrder(product);System.out.println("[静态代理] 记录日志:下单完成");}
}

测试类

java">public class Client {public static void main(String[] args) {OrderService realService = new OrderServiceImpl();OrderService proxy = new OrderServiceStaticProxy(realService);proxy.createOrder("iPhone 15");}
}

输出

[静态代理] 记录日志:开始下单
订单创建成功,商品:iPhone 15
[静态代理] 记录日志:下单完成

JDK代理

通过JDK的InvocationHandlerProxy类动态生成代理对象。
Subject(抽象主题): OrderService接口,定义订单下达。

java">public interface OrderService {void createOrder(String product);
}

RealSubject(真实主题)OrderServiceImpl类,实现下单逻辑。

java">public class OrderServiceImpl implements OrderService {@Overridepublic void createOrder(String product) {System.out.println("订单创建成功,商品:" + product);}
}

Proxy(代理)InvocationHandler类,InvocationHandler接口。

java">public class LogInvocationHandler implements InvocationHandler {private Object target; // 真实对象public LogInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("[JDK动态代理] 记录日志:开始执行方法 " + method.getName());Object result = method.invoke(target, args);System.out.println("[JDK动态代理] 记录日志:方法执行完成");return result;}
}

客户端调用

java">public class Client {public static void main(String[] args) {OrderService realService = new OrderServiceImpl();OrderService proxy = (OrderService) Proxy.newProxyInstance(realService.getClass().getClassLoader(),realService.getClass().getInterfaces(),new LogInvocationHandler(realService));proxy.createOrder("MacBook Pro");}
}

输出结果

[JDK动态代理] 记录日志:开始执行方法 createOrder
订单创建成功,商品:MacBook Pro
[JDK动态代理] 记录日志:方法执行完成

CGLIB动态代理

通过继承目标类生成子类来实现代理(无需接口)
RealSubject(真实主题)OrderServiceImpl类,实现下单逻辑。(这个增强无需接口)

java">// 真实对象(无需接口)
public class OrderService {public void createOrder(String product) {System.out.println("订单创建成功,商品:" + product);}
}

Proxy(代理)MethodInterceptor

java">public class LogMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("[CGLIB代理] 记录日志:开始执行方法 " + method.getName());Object result = proxy.invokeSuper(obj, args);System.out.println("[CGLIB代理] 记录日志:方法执行完成");return result;}
}

客户端

java">public class Client {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(OrderService.class); // 设置父类enhancer.setCallback(new LogMethodInterceptor()); // 设置回调OrderService proxy = (OrderService) enhancer.create(); // 创建代理对象proxy.createOrder("AirPods Pro");}
}

输出结果

[CGLIB代理] 记录日志:开始执行方法 createOrder
订单创建成功,商品:AirPods Pro
[CGLIB代理] 记录日志:方法执行完成

三种代理模式对比

代理类型特点适用场景
静态代理手动编写代理类,直接调用目标对象。代理逻辑简单,目标对象固定。
JDK动态代理基于接口动态生成代理类,通过反射调用目标方法。需要代理接口的实现类。
CGLIB代理通过继承目标类生成子类代理,无需接口。性能较高,但生成代理类较慢。需要代理没有实现接口的类。

适用场景

  • 静态代理:适合代理逻辑简单且目标对象固定的场景。
  • JDK动态代理:适合基于接口的代理(如Spring AOP默认使用JDK代理)。
  • CGLIB代理:适合代理没有接口的类(如Spring AOP在类没有接口时自动切换为CGLIB)。

二、代理模式在框架源码中的运用

Spring Framework 中的 AOP 代理

Spring AOP 通过代理模式实现方法拦截(如事务管理、日志记录),核心类是 ProxyFactoryBean 和动态代理生成器。

核心角色与类:

  • Subject(抽象主题):被代理的接口或类(如 UserService 接口)。
  • RealSubject(真实主题):实际的目标对象(如 UserServiceImpl 类)。
  • Proxy(代理):由 Spring 动态生成的代理对象,具体分为两类:
  • JDK 动态代理:通过 JdkDynamicAopProxy 类生成(代理接口)。
  • CGLIB 代理:通过 ObjenesisCglibAopProxy 类生成(代理类,无接口时使用)。
java">// Spring AOP 生成代理的核心逻辑(ProxyFactoryBean)
public class ProxyFactoryBean {private Object target;          // RealSubject(真实对象)private Class<?>[] interfaces;  // Subject(接口)private Advice advice;          // 增强逻辑(如事务、日志)public Object getObject() {// 根据配置选择生成 JDK 或 CGLIB 代理return createAopProxy().getProxy();}
}

执行流程:

  1. 客户端调用代理对象的方法(如userService.save())。
  2. 代理对象拦截方法调用,执行增强逻辑(如开启事务)。
  3. 代理对象通过反射调用真实对象的方法(UserServiceImpl.save())。
  4. 返回结果前,执行后置增强逻辑(如提交事务)。

MyBatis 中的 Mapper 接口代理

MyBatis 通过代理模式Mapper 接口的方法调用转换为 SQL 执行,核心类是 MapperProxy

核心角色与类

  • Subject(抽象主题)Mapper 接口(如 UserMapper)。
  • RealSubject(真实主题):不存在真实实现类,由代理直接处理逻辑。
  • Proxy(代理)MapperProxy 类,实现 InvocationHandler 接口,动态代理 Mapper 接口。
java">// MyBatis 的 Mapper 代理生成逻辑(MapperProxyFactory)
public class MapperProxyFactory<T> {private final Class<T> mapperInterface;public T newInstance(SqlSession sqlSession) {final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface);return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);}
}// MapperProxy 实现 InvocationHandler
public class MapperProxy<T> implements InvocationHandler {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 将方法调用转换为 SQL 执行(如执行 select * from user where id = ?)return execute(method, args);}
}

执行流程

  1. 客户端调用 UserMapper.findById(1)
  2. MapperProxy 拦截方法调用,解析方法名和参数。
  3. 根据方法名找到对应的 SQL 语句并执行。
  4. 返回数据库查询结果。

三、总结

代理模式的优点

  1. 职责清晰
    • 代理对象负责处理与核心业务无关的逻辑(如权限检查、日志记录、延迟加载等),而真实对象只需关注核心业务逻辑。
    • 符合单一职责原则。
  2. 增强功能
    • 在不修改真实对象的情况下,通过代理对象增强功能(如事务管理、缓存、延迟加载等)。
  3. 解耦
    • 代理模式将客户端与真实对象解耦,客户端只需与代理对象交互,无需直接访问真实对象。
  4. 安全性
    • 代理对象可以控制对真实对象的访问,增加权限检查等安全措施。
  5. 灵活性
    • 动态代理(如 JDK 动态代理、CGLIB 代理)可以在运行时动态生成代理对象,适应不同的需求。

代理模式的缺点

  1. 复杂性增加
    • 引入代理对象会增加系统的复杂性,尤其是动态代理的实现需要理解反射和字节码生成技术。
  2. 性能开销
    • 代理模式可能会引入额外的性能开销,尤其是在频繁调用时(如动态代理的反射调用)。
  3. 代码冗余
    • 静态代理需要为每个真实对象编写代理类,可能导致代码冗余。
  4. 调试困难
    • 动态代理生成的代理类在运行时才存在,调试时可能不如静态代理直观。

代理模式的适用场景

  1. 远程代理
    • 为位于不同地址空间的对象提供本地代表(如 RPC 框架中的远程服务调用)。
  2. 虚拟代理
    • 延迟创建开销较大的对象,直到真正需要时才创建(如图片懒加载、大文件加载)。
  3. 保护代理
    • 控制对敏感对象的访问,基于权限决定是否允许访问(如权限校验)。
  4. 智能引用代理
    • 在访问对象时执行额外操作(如引用计数、懒加载、缓存等)。
  5. AOP(面向切面编程)
    • 在方法调用前后增加通用逻辑(如日志记录、事务管理、性能监控等)。
  6. 延迟初始化
    • 在需要时才初始化对象,节省资源(如 Hibernate 的延迟加载)。
  7. 简化客户端调用
    • 客户端只需与代理对象交互,无需关心真实对象的复杂性(如 MyBatis 的 Mapper 代理)。

http://www.ppmy.cn/server/169051.html

相关文章

Redux中间件redux-thunk和redux-saga的具体区别是什么?

Redux 中间件是增强 Redux 功能的重要工具&#xff0c;redux-thunk 和 redux-saga 是两个常用的中间件&#xff0c;它们在处理异步操作和副作用时提供了不同的方式和理念。以下是两者的具体区别&#xff1a; 1. 概念与设计理念 redux-thunk 简洁&#xff1a;redux-thunk 是一…

vscode远程报错:Remote host key has changed,...

重装了Ubuntu系统之后&#xff0c;由20.04改为22.04&#xff0c;再用vscode远程&#xff0c;就出现了以上报错。 亲测有效的办法 gedit ~/.ssh/known_hosts 打开这个配置文件 删掉与之匹配的那一行&#xff0c;不知道删哪一行的话&#xff0c;就打开第一行这个 /.ssh/confi…

个人博客测试报告

一、项目背景 个人博客系统采用前后端分离的方法来实现&#xff0c;同时使用了数据库来存储相关的数据&#xff0c;同时将其部署到云服务器上。前端主要有四个页面构成&#xff1a;登录页、列表页、详情页以及编辑页&#xff0c;以上模拟实现了最简单的个人博客系统。其结合后…

瑞芯微RV1126部署YOLOv8全流程:环境搭建、pt-onnx-rknn模型转换、C++推理代码、错误解决、优化、交叉编译第三方库

目录 1 环境搭建 2 交叉编译opencv 3 模型训练 4 模型转换 4.1 pt模型转onnx模型 4.2 onnx模型转rknn模型 4.2.1 安装rknn-toolkit 4.2.2 onn转成rknn模型 5 升级npu驱动 6 C++推理源码demo 6.1 原版demo 6.2 增加opencv读取图片的代码 7 交叉编译x264 ffmepg和op…

28、深度学习-自学之路-NLP自然语言处理-做一个完形填空,让机器学习更多的内容程序展示

import sys,random,math from collections import Counter import numpy as npnp.random.seed(1) random.seed(1) f open(reviews.txt) raw_reviews f.readlines() f.close()tokens list(map(lambda x:(x.split(" ")),raw_reviews))#wordcnt Counter() 这行代码的…

Webpack 基础入门

一、Webpack 是什么 Webpack 是一款现代 JavaScript 应用程序的静态模块打包工具。在 Web 开发中&#xff0c;我们的项目会包含各种类型的文件&#xff0c;如 JavaScript、CSS、图片等。Webpack 可以将这些文件打包成一个或多个文件&#xff0c;以便在浏览器中高效加载。它就像…

STM32 看门狗

目录 背景 独立看门狗&#xff08;IWDG&#xff09; 寄存器访问保护 窗口看门狗&#xff08;WWDG&#xff09; 程序 独立看门狗 设置独立看门狗程序 第一步、使能对独立看门狗寄存器的写操作 第二步、设置预分频和重装载值 第三步、喂狗 第四步、使能独立看门狗 喂狗…

【AIDevops】Deepseek驱动无界面自动化运维与分布式脚本系统,初探运维革命之路

声明&#xff1a;笔者当前文章内容仍在构想阶段&#xff0c;仅部分实现 目录 引言 第一部分&#xff1a;基于DeepSeek大模型的单机GPT实现 1. DeepSeek大模型简介 2. 功能概述 3. 项目优势&#xff0c;实现技术栈及实现功能 4. 示例展示 5.腾讯云AI代码助手助力 第二部…