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

Java设计模式-代理模式

代理模式是一种结构型设计模式:为 “某个对象” 提供一种 “代理对象” 以控制对它的访问。调用方不直接引用原对象,而是引用代理;代理在“把请求转给原对象的前/后”附加额外逻辑(缓存、鉴权、延迟加载、事务、日志、熔断、AOP 等)。

一、三种实现方式

实现方式原理与场景优点缺点
静态代理为每一个被代理类 手工编写 一个代理类,编译期就确定代理逻辑简单,易调试代理类爆炸,难维护
JDK 动态代理JDK 反射 运行时生成 代理类,要求被代理类 必须实现接口无侵入,代码量小,官方支持只能代理接口
CGLIB 动态代理ASM 字节码库 运行时生成 被代理类的子类,覆盖方法能代理(无接口也行)不能代理 final 类/方法

注:Spring AOP 默认用 JDK 动态代理;若目标无接口则自动切换到 CGLIB。

二、静态代理示例

  1. 抽象主题:定义业务方法
public interface OrderService {void createOrder(long userId, String itemNo);
}
  1. 真实主题:真正的业务实现(可能部署在远程 / 访问开销很大)
public class OrderServiceImpl implements OrderService {@Overridepublic void createOrder(long userId, String itemNo) {// 业务:落库、扣库存、推送消息 …System.out.printf("为用户 %d 创建订单,商品 %s%n", userId, itemNo);}
}
  1. 代理:给 createOrder 前后加“鉴权 + 耗时统计”
public class OrderServiceProxy implements OrderService {private final OrderService target;  // 被代理对象public OrderServiceProxy(OrderService target) {this.target = target;}@Overridepublic void createOrder(long userId, String itemNo) {preCheck(userId);        // 前置增强long s = System.nanoTime();target.createOrder(userId, itemNo);  // 真正的业务long cost = System.nanoTime() - s;System.out.println("  ==> 耗时(ms) = " + cost / 1_000_000.0);}private void preCheck(long userId) {System.out.println("鉴权… 用户ID = " + userId);}
}
  1. 客户端
OrderService real = new OrderServiceImpl();
OrderService proxy = new OrderServiceProxy(real);
proxy.createOrder(1001L, "A20240522");

输出

鉴权… 用户ID = 1001
为用户 1001 创建订单,商品 A20240522==> 耗时(ms) = 0.52

缺点:接口一多,就要编写 N 个 XxxServiceProxy,后期难以维护。

三、JDK 动态代理示例

核心类:java.lang.reflect.Proxy + InvocationHandler

  1. 编写 InvocationHandler
public class MetricInvocationHandler implements InvocationHandler {private final Object target;        // 真正业务对象public MetricInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {long s = System.nanoTime();Object ret = method.invoke(target, args);System.out.printf("方法 %s 执行耗时(ms): %.2f%n",method.getName(), (System.nanoTime() - s) / 1_000_000.0);return ret;}
}
  1. 生成并调用代理
OrderService real = new OrderServiceImpl();
OrderService proxy = (OrderService) Proxy.newProxyInstance(OrderService.class.getClassLoader(),   // 类加载器new Class<?>[]{OrderService.class},    // 需要实现的接口new MetricInvocationHandler(real));proxy.createOrder(1002L, "B20240522");

输出

为用户 1002 创建订单,商品 B20240522
方法 createOrder 执行耗时(ms): 0.30

注:JDK 动态代理只能在 运行时 生成代理对象,无法代理类本身的方法(如 OrderServiceImpl 中未定义在接口的方法)。

四、CGLIB 动态代理示例

引用库:cglib 或 spring-core(已含 cglib)

public class CglibMetricInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)throws Throwable {long s = System.nanoTime();Object ret = proxy.invokeSuper(obj, args);   // 调用父类(真实类)方法System.out.printf("CGLIB: %s 耗时(ms) %.2f%n",method.getName(), (System.nanoTime() - s) / 1_000_000.0);return ret;}
}

使用

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderServiceImpl.class);                 // 被代理类
enhancer.setCallback(new CglibMetricInterceptor());
OrderServiceImpl proxy = (OrderServiceImpl) enhancer.create();
proxy.createOrder(1003L, "C20240522");

五、常见 UML 类图

                 +----------------+------>  | <<interface>> |uses       |  Subject      |RealSubject   +-------+-------+/ Proxy              ||+--------------+-------------+|                            |+-----+-------+             +------+------+| RealSubject | implements  |   Proxy     |+-------------+             +-------------+

六、与其他模式的关系

  • 装饰器模式:都通过“组合”包装对象,但装饰器更强调“功能叠加”,而不控制访问。
  • 门面模式:门面做“高层封装”,代理做“同层”的替代。
  • 桥接模式:两者都解耦抽象与实现,但侧重点不同。
  • Spring AOP:本质就是动态代理(JDK / CGLIB)。

七、最佳实践

  1. 延迟加载 —— Hibernate / MyBatis:只有在第一次调用时才真正去查库。
  2. 保护代理 —— 权限框架:代理层统一鉴权。
  3. 远程代理 —— RPC:Stub 封装网络通信。
  4. 缓存代理 —— 读取前查缓存,写后更新缓存。
  5. 日志/监控 —— 微服务 Sidecar、链路追踪。
http://www.xdnf.cn/news/16183.html

相关文章:

  • 《Angular+Spring Boot:ERP前端采购销售库存协同架构解析》
  • FalconFS: Distributed File System for Large-Scale Deep Learning Pipeline——论文阅读
  • ReVQ (Quantize-then-Rectify,量化后修正)
  • [MMU] Table walk flow详解
  • IAR编辑器如何让左侧的工具栏显示出来?
  • MCP工具开发实战:打造智能体的“超能力“
  • GaussDB 逻辑备份实操
  • windows11安装wsl装Ubuntu到D盘及可视化页面,安装docker及宝塔面板
  • 初识opencv03——图像预处理2
  • Day 20:奇异值SVD分解
  • Python Day15 面向对象核心特性笔记 及 例题分析
  • 数组toString方法及类型检测修复方案
  • Linux 内核基础统简全解:Kbuild、内存分配和地址映射
  • 【推荐100个unity插件】Animator 的替代品?—— Animancer Pro插件的使用介绍
  • 同花顺前端潜在面试题目与答案
  • 星慈光编程虫2号小车讲解第一篇--向前向后
  • 力扣1287:有序数组中出现次数超过25%的元素
  • 背包DP之分组背包
  • 嵌入式通信知识串讲:从同步 / 异步传输到 UART 协议 STM32F103 硬件解析
  • ​Excel——SUMPRODUCT 函数
  • 基于CloudBase+React+CodeBudddy的云上智能睡眠应用开发实践
  • PCL 间接平差拟合球
  • 基于20和28 nm FPGAs的实现多通道、低非线性时间到数字转换器
  • 变量和函数底层工作原理
  • T-RO顶刊|单视角“找相似”,大阪大学提出新型点云描述符(C-FPFH),杂乱场景一抓一个准!
  • 0724 双向链表
  • C语言(十)
  • 移动端自动化Appium框架
  • 清除浮动以及原理
  • 2025年6月GESP(C++六级):学习小组