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

什么是 Java 中的动态代理?

Java 中的动态代理详解

1. 动态代理概述

动态代理是 Java 在运行时动态创建代理类和代理对象的机制。它允许在不修改原始类代码的情况下,对方法调用进行拦截、增强或修改。

核心特点

  • 运行时生成:代理类在程序运行时动态创建,而非编译时
  • 无侵入性:不需要修改被代理类的源代码
  • 灵活性:可以统一处理多个类的相同逻辑

2. 动态代理 vs 静态代理

// 静态代理:编译时确定interfaceUserService{voidaddUser(Stringname);}classUserServiceImplimplementsUserService{publicvoidaddUser(Stringname){System.out.println("添加用户: "+name);}}classUserServiceProxyimplementsUserService{privateUserServicetarget;publicUserServiceProxy(UserServicetarget){this.target=target;}publicvoidaddUser(Stringname){System.out.println("前置增强:日志记录");target.addUser(name);System.out.println("后置增强:提交事务");}}// 使用UserServiceproxy=newUserServiceProxy(newUserServiceImpl());proxy.addUser("张三");

静态代理的问题

  • 每个接口需要单独编写代理类
  • 代码重复,维护困难
  • 不够灵活

3. Java 动态代理实现

3.1 基本结构

importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;// 1. 定义接口interfaceUserService{voidaddUser(Stringname);voiddeleteUser(Stringname);}// 2. 实现真实对象classUserServiceImplimplementsUserService{publicvoidaddUser(Stringname){System.out.println("添加用户: "+name);}publicvoiddeleteUser(Stringname){System.out.println("删除用户: "+name);}}// 3. 创建调用处理器classLogInvocationHandlerimplementsInvocationHandler{privateObjecttarget;// 被代理对象publicLogInvocationHandler(Objecttarget){this.target=target;}@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{// 前置增强System.out.println("【前置】调用方法: "+method.getName());System.out.println("【前置】参数: "+Arrays.toString(args));longstart=System.currentTimeMillis();// 调用真实对象的方法Objectresult=method.invoke(target,args);longend=System.currentTimeMillis();// 后置增强System.out.println("【后置】方法执行耗时: "+(end-start)+"ms");System.out.println("【后置】返回值: "+result);returnresult;}}// 4. 使用动态代理publicclassDynamicProxyDemo{publicstaticvoidmain(String[]args){// 创建真实对象UserServicerealService=newUserServiceImpl();// 创建代理对象UserServiceproxy=(UserService)Proxy.newProxyInstance(realService.getClass().getClassLoader(),// 类加载器realService.getClass().getInterfaces(),// 接口列表newLogInvocationHandler(realService)// 调用处理器);// 使用代理对象proxy.addUser("张三");System.out.println("----------");proxy.deleteUser("李四");}}

输出结果

【前置】调用方法: addUser 【前置】参数: [张三] 添加用户: 张三 【后置】方法执行耗时: 0ms 【后置】返回值: null ---------- 【前置】调用方法: deleteUser 【前置】参数: [李四] 删除用户: 李四 【后置】方法执行耗时: 0ms 【后置】返回值: null

3.2 Proxy.newProxyInstance 参数说明

Objectproxy=Proxy.newProxyInstance(ClassLoaderloader,// 类加载器:通常使用被代理类的类加载器Class<?>[]interfaces,// 接口数组:代理对象需要实现的接口InvocationHandlerh// 调用处理器:方法调用的拦截逻辑);

4. InvocationHandler 接口

publicinterfaceInvocationHandler{/** * @param proxy 代理对象本身 * @param method 被调用的方法 * @param args 方法参数 * @return 方法返回值 */Objectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable;}

invoke 方法执行流程

客户端调用代理方法 ↓ Proxy 拦截调用 ↓ InvocationHandler.invoke() ↓ 前置处理(日志、权限等) ↓ method.invoke(target, args) // 调用真实对象 ↓ 后置处理(事务、统计等) ↓ 返回结果

5. 实际应用场景

5.1 日志记录

classLoggingHandlerimplementsInvocationHandler{privateObjecttarget;publicLoggingHandler(Objecttarget){this.target=target;}@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{System.out.println("[LOG] "+newDate()+" - 调用 "+method.getName());try{Objectresult=method.invoke(target,args);System.out.println("[LOG] "+method.getName()+" 执行成功");returnresult;}catch(Exceptione){System.out.println("[LOG] "+method.getName()+" 执行失败: "+e.getMessage());throwe;}}}

5.2 事务管理

classTransactionHandlerimplementsInvocationHandler{privateObjecttarget;@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{if(method.getName().startsWith("update")||method.getName().startsWith("delete")||method.getName().startsWith("insert")){System.out.println("开启事务");try{Objectresult=method.invoke(target,args);System.out.println("提交事务");returnresult;}catch(Exceptione){System.out.println("回滚事务");throwe;}}else{returnmethod.invoke(target,args);}}}

5.3 权限控制

classSecurityHandlerimplementsInvocationHandler{privateObjecttarget;privateStringcurrentUser;publicSecurityHandler(Objecttarget,StringcurrentUser){this.target=target;this.currentUser=currentUser;}@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{// 检查是否有权限if(method.isAnnotationPresent(RequiresAdmin.class)){if(!"admin".equals(currentUser)){thrownewSecurityException("无权限访问: "+method.getName());}}returnmethod.invoke(target,args);}}

5.4 缓存代理

classCacheHandlerimplementsInvocationHandler{privateObjecttarget;privateMap<String,Object>cache=newHashMap<>();@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{Stringkey=method.getName()+Arrays.toString(args);// 检查缓存if(cache.containsKey(key)){System.out.println("从缓存获取: "+key);returncache.get(key);}// 调用真实方法Objectresult=method.invoke(target,args);cache.put(key,result);returnresult;}}

6. 链式代理(多重代理)

// 创建多个代理UserServicerealService=newUserServiceImpl();// 第一层:日志代理UserServicelogProxy=(UserService)Proxy.newProxyInstance(realService.getClass().getClassLoader(),realService.getClass().getInterfaces(),newLogInvocationHandler(realService));// 第二层:事务代理(代理日志代理)UserServicetxProxy=(UserService)Proxy.newProxyInstance(logProxy.getClass().getClassLoader(),logProxy.getClass().getInterfaces(),newTransactionHandler(logProxy));// 调用txProxy.addUser("张三");

执行顺序

事务前置 → 日志前置 → 真实方法 → 日志后置 → 事务后置

7. 动态代理的局限性

7.1 只能代理接口

// ❌ 错误:不能代理类classNoInterface{publicvoiddoSomething(){}}// 编译错误:NoInterface 不是接口NoInterfaceproxy=(NoInterface)Proxy.newProxyInstance(NoInterface.class.getClassLoader(),NoInterface.class.getInterfaces(),// 空数组handler);

7.2 解决方案:CGLIB

// CGLIB 可以代理类(通过继承)Enhancerenhancer=newEnhancer();enhancer.setSuperclass(NoInterface.class);enhancer.setCallback(newMethodInterceptor(){@OverridepublicObjectintercept(Objectobj,Methodmethod,Object[]args,MethodProxyproxy)throwsThrowable{System.out.println("前置增强");Objectresult=proxy.invokeSuper(obj,args);System.out.println("后置增强");returnresult;}});NoInterfaceproxy=(NoInterface)enhancer.create();

8. 动态代理在框架中的应用

Spring AOP

// Spring 使用动态代理实现 AOP@Service@TransactionalpublicclassUserService{@TransactionalpublicvoidcreateUser(Stringname){// Spring 会自动创建代理,处理事务}}

MyBatis Mapper

// MyBatis 使用动态代理创建 Mapper 接口实现UserMappermapper=sqlSession.getMapper(UserMapper.class);// mapper 是动态代理对象,方法调用会转换为 SQL 执行Useruser=mapper.selectById(1);

Retrofit

// Retrofit 使用动态代理创建 HTTP 接口publicinterfaceApiService{@GET("users/{id}")Call<User>getUser(@Path("id")intid);}ApiServiceservice=retrofit.create(ApiService.class);// service 是动态代理对象

9. 总结

特性说明
创建时机运行时动态创建
代理对象实现 InvocationHandler 接口
核心方法Proxy.newProxyInstance()
拦截机制InvocationHandler.invoke()
代理类型只能代理接口
主要用途AOP、日志、事务、权限控制等
优势灵活、无侵入、统一处理
局限不能代理 final 类和方法

动态代理是 Java 实现面向切面编程(AOP)的核心技术,广泛应用于各种框架中!

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

相关文章:

  • Java 中 hashCode 和 equals 方法是什么?它们与 == 操作符有什么区别?
  • 《计算机是怎样跑起来的》————让写算法跟呼吸一样简单
  • 购物卡回收的三种热门方法整理 - 京回收小程序
  • ChatPPT Nano Banana Pro · Magic模式深度解析 ——重新定义“所想即所得”的PPT智能编辑
  • ARM Cortex-A7(IMX6ULL)嵌入式裸机开发指南:从点灯到中断 - 实践
  • 大润发购物卡回收靠谱的3个主流渠道 - 京回收小程序
  • 天猫超市购物卡回收常见三种方法及流程解析 - 京回收小程序
  • 最近在调试西门子808D数控系统的机械手刀库,整个过程虽然有点复杂,但还挺有意思的。今天就来分享一下我的调试经验,顺便贴点代码,希望能帮到有需要的朋友
  • 镜像孪生驱动的视频孪生升级版水利电力三维态势控制中枢白皮书---依托矩阵视频融合架构、统一三维坐标基准构建技术、动态误差修正模型与风险交汇时间解算算法形成的空间级前向布控平台-
  • 2026年公司起名机构推荐榜单:十大专业品牌深度测评,企业选型必看 - 博客万
  • 视频孪生之上,是镜像孪生镜像视界三维空间控制作战体系---基于镜像视界(浙江)科技有限公司矩阵视频融合、Pixel-to-3D 反演引擎、三维轨迹建模体系与趋势级风险推演算法构建的全域主动压制平
  • 从春晚舞台到万家灯火:菁彩Vivid三度携手央视频,以沉浸体验点亮中国年 - 博客万
  • 6大方法禁止win11自动更新
  • 进口维生素d3十大品牌揭晓,维生素d3哪个牌子成分安全?复配K2,锁钙护血管更安心 - 博客万
  • 免费招聘平台TOP榜盘点,前三名免费查看简历 - 博客万
  • 目前最靠谱的招聘网站?2026权威测评与真实口碑 - 博客万
  • 【Docker高级篇】吃透Docker CI/CD集成:从代码提交到镜像部署,一步到位不踩坑
  • 【Docker高级篇】吃透容器编排:Swarm vs K8s 核心差异,为后续K8s学习打牢基础
  • 【Docker高级篇】新手也能懂的应用安全:为什么不能用root?镜像怎么扫漏洞?
  • 大数据架构数据流水线:从采集到分析的完整设计
  • 基于博途1200PLC + HMI病床呼叫控制系统仿真探索
  • A2UI协议,打破Agent交互壁垒,让智能系统自主“搭建”界面 - 指南
  • YOLO26涨点改进 | 独家创新,注意力改进篇 | AAAI 2025 | YOLO26引入 DRM 防御优化模块,进行特征优化/特征增强,助力目标检测、图像分类、图像分割有效涨点
  • dokuwikiAPI 探索器
  • 哨兵机制(sentinel)的原理
  • Redis客观下线
  • 一文搞懂超详细ubuntu22.04部署k8s1.28高可用(一)【多master+keepalived+nginx实现负载均衡】:核心原理+实战案例
  • 61 二分查找
  • 解决DokuWiki JSONRPC 403错误
  • COMSOL 两相流 THM 热流固耦合模型:探索与实践