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

动态代理详解

想要更加透彻的理解动态代理,首先要熟悉下静态代理

一、静态代理

总结来说:目标类和代理类实现了相同的接口在代理类中依赖了目标类,代理类的方法中调用了目标类的方法,并做了一些增强性的工作。

1、实现静态代理(demo)

要求,在某个类执行类中的方法时,上下文添加一些日志记录

1)定义接口

publicinterfaceCalculate{intadd(intnum1,intnum2);}

2)定义被代理类

publicclassCalculatorimplementsCalculate{@Overridepublicintadd(intnum1,intnum2){System.out.println(num1+"+"+num2+"="+num1+num2);returnnum1+num2;}}

3)定义代理类

publicclassCalculatorWithLogimplementsCalculate{privateCalculatorcalculator;publicCalculatorWithLog(Calculatorcalculator){this.calculator=calculator;}@Overridepublicintadd(intnum1,intnum2){System.out.println("add前添加一点日志");intsum=calculator.add(num1,num2);System.out.println("add后添加一点日志");returnsum;}}

4)测试

publicstaticvoidmain(String[]args){Calculatorcalculator=newCalculator();CalculatorWithLogcalculatorWithLog=newCalculatorWithLog(calculator);calculatorWithLog.add(1,2);}

2、静态代理的缺陷

  • 程序员要手动为每一个目标类编写对应的代理类。如果当前系统已经有成百上千个类,工作量太大了
  • 当接口改变时,所有的代理类都需要进行相应的变化,太过于耦合
    所以,现在我们的努力方向是:如何少写或者不写代理类,却能完成代理功能?

二、动态代理

参考B站视频:https://www.bilibili.com/video/BV1HZ4y1p7F1?p=7&vd_source=7f24cfc4e2c729f062d4a78d958bfcdf

动态代理指的是:在程序的执行过程中,使用jdk的反射机制,创建代理对象,并动态的指定代理的目标类

动态代理的实现方式常用有两种:
- 使用JDK代理
- 通过CDLIB代理

1、jdk动态代理

jdk动态代理是基于Java的反射机制实现的,使用jdk反射包下的Proxy和InvocationHandler实现代理对象的动态创建。(jdk动态代理要求目标对象必须实现接口

1)InvocationHandler接口

方法拦截处理器,接口中就一个方法 :invoke(),你的代理类要完成的功能就写在invoke()中

  • 调用目标类的方法
  • 功能增强,在目标方法调用时,增加功能

方法原型:

// Object proxy:jdk创建的代理对象,无需赋值// Method method:目标类中的方法// Object[] args:目标类中方法的参数publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)

2)Method类

通过Method可以执行某个目标类的方法

3)proxy类

是最核心的一个类,使用静态方法 newProxyInstance() ,创建代理对象
方法原型:

// ClassLoader loader:类加载器,负责向内存中加载对象(需要使用目标对象的类加载器)// Class<?>[] interfaces:目标对象实现的接口// InvocationHandler h:方法拦截器,我们自己写的代理类需要实现的功能返回值就是代理对象publicstaticObjectnewProxyInstance(ClassLoaderloader,Class<?>[]interfaces,InvocationHandlerh)

4)动态代理的实现步骤

  • 创建接口,定义目标类要完成的功能
publicinterfaceCalculate{intadd(intnum1,intnum2);}
  • 创建目标类,实现该接口
publicclassCalculatorimplementsCalculate{@Overridepublicintadd(intnum1,intnum2){System.out.println(num1+"+"+num2+"="+(num1+num2));returnnum1+num2;}}
  • 创建 InvocationHandler 接口的实现类,在invoke()方法中完成代理类的功能
publicclassMyInvocationHandlerimplementsInvocationHandler{privateObjecttarget=null;//动态代理,目标对象是活动的,不是固定的,需要传入进来publicMyInvocationHandler(Objecttarget){this.target=target;}@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{Objectres=method.invoke(target,args);System.out.println("代理对象中进行功能增强....");returnres;}}
  • 使用proxy类的newProxyInstance()方法,创建代理对象,并把返回值转成接口类型
publicclassTest{publicstaticvoidmain(String[]args){//1、创建目标对象Calculatorcalculator=newCalculator();//2、创建invocationHandler对象InvocationHandlerinvocationHandler=newMyInvocationHandler(calculator);CalculateproxyInstance=(Calculate)Proxy.newProxyInstance(calculator.getClass().getClassLoader(),calculator.getClass().getInterfaces(),invocationHandler);proxyInstance.add(1,3);}}

5)jdk动态代理的执行流程

断点打到InvocationHandler的invoke方法中,查看method和args参数

Proxy.newProxyInstance() 返回的对象,就是jdk创建的一个代理对象

6)动态代理能做什么?

在不改变原来目标方法功能的前提下,可以在代理中增强自己功能代码。

在程序开发中的意义:
- 你所在的项目中,有一个功能是其他人写好的,你可以使用,但是不能满足我的需求,我需要再增加点代码,这次就需要使用代理来进行代码的增强,而不用改原来的代码

2、cglib动态代理

cglib通过继承的方式生成目标类的子类作为代理。CGLIB 可以代理没有实现接口的类。

工作原理

  • 目标类不能是 final 的,且被代理的方法也不能是 final 或 static(因为无法被重写)。

  • 使用 CGLIB 的 Enhancer 类来设置父类(目标类)和回调(MethodInterceptor 接口的实现)。

  • 在 MethodInterceptor 的 intercept 方法中编写增强逻辑,并通过 MethodProxy.invokeSuper() 调用父类(目标类)的方法。

  • 生成的代理类是目标类的子类,重写了非 final 的方法,在重写的方法中执行拦截逻辑。

使用示例

// 目标类(无接口)publicclassUserService{publicvoidaddUser(Stringname){System.out.println("添加用户:"+name);}}// CGLIB 方法拦截器publicclassLogInterceptorimplementsMethodInterceptor{@OverridepublicObjectintercept(Objectobj,Methodmethod,Object[]args,MethodProxyproxy)throwsThrowable{System.out.println("日志开始...");Objectresult=proxy.invokeSuper(obj,args);// 调用父类方法System.out.println("日志结束...");returnresult;}}// 使用代理Enhancerenhancer=newEnhancer();enhancer.setSuperclass(UserService.class);enhancer.setCallback(newLogInterceptor());UserServiceproxy=(UserService)enhancer.create();proxy.addUser("李四");

优点

可代理没有接口的类,适用范围更广。
使用 MethodProxy.invokeSuper() 调用父类方法,比 JDK 反射更快(内部使用了 FastClass 机制)。

缺点

需要引入 CGLIB 库(Spring 已将其打包在内)。
无法代理 final 类或 final 方法。
对于继承层次深的类,可能生成较大的代理类。

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

相关文章:

  • 通过git上传代码到gitlab(包含第一次上传)小结
  • wow-time时间操作说明
  • Agentic插件系统:扩展平台功能的终极架构设计指南
  • M3U8 在线调试神器!m3u8live.cn让 HLS 流测试更高效
  • HLS 开发必备!详解m3u8live.cn在线播放器的使用与价值
  • 【Index to Lectures or Courses】
  • 如何用代码定义架构:深入探索LikeC4项目
  • WebRTC系列-网络之带宽估计和码率估计(2)接收端带宽估计
  • 如何在Linux终端使用sc-im?新手入门的完整指南
  • mmdetection目标检测API封装:Python SDK开发全攻略
  • 终极Geocoder安全指南:保护API密钥与高效管理服务配额的完整方法
  • wow-byte-array数组操作说明
  • ffmpeg将mp4转换为swf、视频格式、m3u8等
  • 从零开始学习DeepSeek-Prover-V1.5-SFT:面向数学爱好者的入门教程
  • 如何在 React 项目中快速集成 Google Map React:10分钟上手教程
  • 如何快速上手LedisDB:高性能NoSQL数据库的完整指南
  • 如何构建团队密码管理系统:gopass的设计哲学与架构深度解析
  • 随心所欲画草神器:3DMAX种草画笔GrassPainter
  • 电子商务专业毕业生职业发展路径与核心能力构建研究
  • 如何使用CoreRT:.NET Core终极AOT编译优化指南
  • 目前最全的计算机视觉公开数据集汇总 持续更新 400+数据集
  • WHAT - 浏览器缓存机制系列(二)强缓存、协商缓存和启发式缓存
  • CausalML高级技巧:特征选择与因果效应异质性分析
  • ROS以及工控机环境配置
  • Gorilla技术播客系列:与AI先驱探讨函数调用的未来
  • 去毛刺机设计(机械毕业设计)
  • 为什么我的电脑不能升级Windows 11?终极兼容性检测工具深度解析
  • OCRmyPDF内存优化:处理大型PDF文件的内存管理技巧
  • Leetcode_155. 最小栈
  • 软考中级--数据库系统工程师 备考建议和考试注意事项