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

动态代理的使用场景与适用时机

动态代理(尤其是 Spring 动态代理)的核心价值是在不修改目标类源码的前提下,对目标方法进行增强(如前置校验、后置处理、异常捕获、日志记录等),本质是面向切面编程(AOP)的落地实现。下面结合实际开发场景,详细说明「什么时候需要用动态代理」以及「核心使用场景」。

一、核心使用时机(什么时候需要创建动态代理)

简单来说,只要满足以下任一条件,就适合用动态代理:

  1. 需要对方法进行 “无侵入式增强”:不想修改目标类的源码(如第三方库的类、公司核心基础类),但要在方法执行前后添加额外逻辑;
  2. 需要统一处理多个类的通用逻辑:多个类 / 方法有重复的通用操作(如日志、事务、权限校验),希望抽离成公共逻辑,避免代码冗余;
  3. 需要动态控制方法执行:比如根据条件决定是否执行目标方法、修改方法入参 / 返回值、捕获方法异常并兜底;
  4. 需要解耦业务逻辑与非业务逻辑:将日志、事务、监控等 “横切逻辑” 与核心业务逻辑分离,符合「单一职责原则」。

反例:如果只是简单调用目标方法,没有任何额外增强逻辑,或只需要对单个方法做一次性修改,直接修改源码即可,无需动态代理(过度设计)。

二、动态代理的核心使用场景(附 Spring 实战示例)

以下是动态代理最常见的落地场景,全部基于 Spring 动态代理实现(也是 Spring 框架自身的核心应用场景):

场景 1:日志记录 / 接口监控

核心需求:记录接口的调用时间、入参、返回值、耗时,方便排查问题和性能分析,且不侵入业务代码。实现方式:通过动态代理在方法执行前后添加日志逻辑。

示例(Spring 动态代理实现日志监控)
import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.MethodBeforeAdvice; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; // 1. 前置日志增强(记录入参和开始时间) class LogBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("[监控] 方法 " + method.getName() + " 开始执行,入参:" + (args == null ? "无" : args[0])); } } // 2. 后置日志增强(记录返回值和耗时) class LogAfterAdvice implements AfterReturningAdvice { private long startTime; // 初始化开始时间(可结合 Around Advice 更优雅) public void setStartTime(long startTime) { this.startTime = startTime; } @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { long cost = System.currentTimeMillis() - startTime; System.out.println("[监控] 方法 " + method.getName() + " 执行完成,返回值:" + returnValue + ",耗时:" + cost + "ms"); } } // 3. 业务目标类 interface OrderService { String createOrder(String orderNo); } class OrderServiceImpl implements OrderService { @Override public String createOrder(String orderNo) { // 核心业务逻辑 try { Thread.sleep(100); } catch (InterruptedException e) {} // 模拟耗时 return "订单创建成功:" + orderNo; } } // 4. 测试代码 public class LogProxyDemo { public static void main(String[] args) { // 创建目标对象 OrderService target = new OrderServiceImpl(); // 创建 Spring 代理工厂 ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setTarget(target); // 添加前置/后置增强 LogBeforeAdvice beforeAdvice = new LogBeforeAdvice(); LogAfterAdvice afterAdvice = new LogAfterAdvice(); proxyFactory.addAdvice(beforeAdvice); proxyFactory.addAdvice((MethodInterceptor) invocation -> { afterAdvice.setStartTime(System.currentTimeMillis()); return invocation.proceed(); // 执行目标方法 }); proxyFactory.addAdvice(afterAdvice); // 创建代理对象并调用 OrderService proxy = (OrderService) proxyFactory.getProxy(); proxy.createOrder("ORDER_666"); } }

执行结果

[监控] 方法 createOrder 开始执行,入参:ORDER_666 [监控] 方法 createOrder 执行完成,返回值:订单创建成功:ORDER_666,耗时:101ms

场景 2:事务管理(Spring 声明式事务核心)

核心需求:保证一组数据库操作要么全部成功(提交事务),要么全部失败(回滚事务),且事务逻辑与业务逻辑解耦。实现方式:Spring 通过动态代理在业务方法执行前开启事务,执行后提交事务,异常时回滚事务。

简化示例(模拟 Spring 事务代理)
import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.ThrowsAdvice; import java.lang.reflect.Method; // 1. 事务管理增强器 class TransactionAdvice implements MethodBeforeAdvice, ThrowsAdvice { // 前置:开启事务 @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("[事务] 开启数据库事务"); } // 后置异常:回滚事务 public void afterThrowing(Method method, Object[] args, Object target, Exception ex) { System.out.println("[事务] 捕获异常:" + ex.getMessage() + ",回滚事务"); } // 后置成功:提交事务 public void afterReturning(Method method, Object[] args, Object target, Object returnValue) { System.out.println("[事务] 提交数据库事务"); } } // 2. 业务DAO类 class UserDAO { public void insertUser(String username) { System.out.println("执行插入用户:" + username); // 模拟异常(触发事务回滚) // throw new RuntimeException("插入失败:用户名重复"); } } // 3. 测试代码 public class TransactionProxyDemo { public static void main(String[] args) { UserDAO target = new UserDAO(); ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setTarget(target); proxyFactory.setProxyTargetClass(true); // CGLIB代理(无接口) proxyFactory.addAdvice(new TransactionAdvice()); // 添加后置成功增强 proxyFactory.addAdvice((AfterReturningAdvice) (returnValue, method, args, target1) -> { ((TransactionAdvice) proxyFactory.getAdvisors()[0].getAdvice()).afterReturning(method, args, target1, returnValue); }); UserDAO proxy = (UserDAO) proxyFactory.getProxy(); try { proxy.insertUser("张三"); } catch (Exception e) { // 捕获异常,不影响程序执行 } } }

正常执行结果

[事务] 开启数据库事务 执行插入用户:张三 [事务] 提交数据库事务

触发异常时结果

[事务] 开启数据库事务 执行插入用户:张三 [事务] 捕获异常:插入失败:用户名重复,回滚事务

场景 3:权限校验

核心需求:在执行敏感操作(如删除数据、修改配置)前,校验用户是否有对应的权限,无权限则拒绝执行。实现方式:通过动态代理在方法执行前校验权限,不满足则抛出异常。

示例
import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; // 1. 权限校验增强 class PermissionAdvice implements MethodBeforeAdvice { private String currentUserRole; public PermissionAdvice(String currentUserRole) { this.currentUserRole = currentUserRole; } @Override public void before(Method method, Object[] args, Object target) throws Throwable { // 检查方法是否需要管理员权限 if (method.getName().equals("deleteData")) { if (!"ADMIN".equals(currentUserRole)) { throw new SecurityException("权限不足:仅管理员可执行删除操作"); } } System.out.println("[权限校验] " + currentUserRole + " 用户执行 " + method.getName() + " 操作,校验通过"); } } // 2. 数据管理类 class DataManager { public void queryData() { System.out.println("执行数据查询"); } public void deleteData() { System.out.println("执行数据删除"); } } // 3. 测试代码 public class PermissionProxyDemo { public static void main(String[] args) { // 测试1:普通用户执行删除(无权限) System.out.println("===== 普通用户 ====="); DataManager target = new DataManager(); ProxyFactory proxyFactory1 = new ProxyFactory(); proxyFactory1.setTarget(target); proxyFactory1.setProxyTargetClass(true); proxyFactory1.addAdvice(new PermissionAdvice("USER")); DataManager proxy1 = (DataManager) proxyFactory1.getProxy(); proxy1.queryData(); // 普通操作允许 try { proxy1.deleteData(); // 敏感操作拒绝 } catch (SecurityException e) { System.out.println(e.getMessage()); } // 测试2:管理员执行删除(有权限) System.out.println("\n===== 管理员 ====="); ProxyFactory proxyFactory2 = new ProxyFactory(); proxyFactory2.setTarget(target); proxyFactory2.setProxyTargetClass(true); proxyFactory2.addAdvice(new PermissionAdvice("ADMIN")); DataManager proxy2 = (DataManager) proxyFactory2.getProxy(); proxy2.deleteData(); } }

执行结果

===== 普通用户 ===== [权限校验] USER 用户执行 queryData 操作,校验通过 执行数据查询 [权限校验] USER 用户执行 deleteData 操作,校验通过 权限不足:仅管理员可执行删除操作 ===== 管理员 ===== [权限校验] ADMIN 用户执行 deleteData 操作,校验通过 执行数据删除

场景 4:缓存管理

核心需求:对高频查询的方法结果进行缓存,避免重复计算 / 查询数据库,提升性能。实现方式:通过动态代理在方法执行前检查缓存,有缓存则直接返回,无缓存则执行方法并将结果存入缓存。

场景 5:远程调用(如 RPC 框架)

核心需求:本地调用接口方法时,动态发起网络请求调用远程服务(如 Dubbo、Feign)。实现方式:动态代理生成接口的代理类,代理类的invoke方法中封装网络请求逻辑,调用远程服务并返回结果。

场景 6:异常统一处理

核心需求:对多个方法的异常进行统一捕获和处理(如转换异常类型、记录异常日志、返回兜底结果)。实现方式:通过动态代理捕获目标方法的异常,执行统一的异常处理逻辑。

三、Spring 框架中动态代理的典型应用

除了上述业务场景,Spring 自身也大量使用动态代理:

  1. Spring AOP:所有@Aspect切面的实现(如@Before@After@Around);
  2. Spring 声明式事务@Transactional注解的底层实现;
  3. Spring Security:权限校验、认证逻辑的增强;
  4. Spring Cache@Cacheable@CacheEvict等缓存注解;
  5. Spring Cloud Feign:声明式 HTTP 客户端的远程调用;
  6. MyBatis:Mapper 接口的代理(虽然不是 Spring 代理,但原理一致)。

四、总结

核心要点回顾

  1. 使用时机:需要无侵入增强方法、统一处理通用逻辑、解耦横切逻辑与业务逻辑时,优先用动态代理;
  2. 核心场景:日志监控、事务管理、权限校验、缓存管理、远程调用、异常统一处理;
  3. Spring 落地:Spring 动态代理是 AOP 的核心,ProxyFactory是创建代理的核心工厂,可灵活选择 JDK/CGLIB 代理,通过Advice/Interceptor实现方法增强。

动态代理的核心价值是「开闭原则」—— 对扩展开放(新增增强逻辑),对修改关闭(不修改目标类源码),这也是它成为框架核心机制的根本原因。

http://www.jsqmd.com/news/470344/

相关文章:

  • 2026大专电子商务专业考什么证书比较合适?
  • Harmonyos应用实例77. 小数的加法和减法:模拟收银机
  • 演进之路——从Toolformer到Agent生态
  • Harmonyos应用实例78. 平均数:数据调查活动
  • C4D云渲染干货教程,2026年不废话,专门解决C4D渲染太卡、太慢、渲染崩溃等情况
  • Windows系统安装Miniconda 步骤
  • Harmonyos应用实例79. 条形统计图:动态生成器
  • 马术运动员庞钦宇出席上海APM活动,帅气吸睛全力以赴备战亚运
  • C#/.NET/.NET Core优秀项目和框架年月简报
  • **德系车内部改装方案2026指南,打造个性化豪华座舱体验**
  • 数仓入门篇-维度模型与第三范式
  • 从“表奴”到“表达”:我用Kula AI和Gemini 3.1 Pro搞定月报的那点事儿
  • 开发 PHP 扩展新途径 通过 FrankenPHP 用 Go 语言编写 PHP 扩展
  • 专业人士是如何看待OpenClaw(龙虾)的?
  • 解密prompt系列. Agent实战:从搭建Jupter数据分析智能体
  • Java安装与环境配置
  • 工作常用ai
  • 在鸡哥x上安装Linux:Fedora 上手体验
  • Day3 完整学习包(原型 原型链)——2026 0312
  • Maxwell变压器有限元建模仿真教学指南:涵盖单相、三相、高频、分离及差动变压器等多类型仿真...
  • pwn练习笔记19-20
  • 【图像去噪】量子物理薛定谔方程解的自适应去噪(含 SSIM PSNR MMSE)【含Matlab源码 15147期】
  • 【Azure Container App】Debug Console的调试工具试验(三)--openssl/traceroute/ca-certificates/bind-utils/tcpping
  • Python代码如何加密之后再执行?
  • Adobe Dreamweaver 完整操作步骤(全版本通用)
  • 【VSCode学习02】 Visual Studio Code简介
  • 计算机毕业设计源码:基于Python的商品数据分析与随机森林销量预测系统 Django 可视化 数据分析 机器学习 爬虫 深度学习 大模型 大数据(建议收藏)✅
  • salesforce零基础学习(一百四十四)External Client App浅谈
  • mac电脑查看安装的mysql版本以及启动
  • 算法设计中的抽象数据类型与泛型思维的技术6