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

关于动态代理的个人记录

静态代理

代理类持有一个目标对象的引用,所以本质上是代理对象将调用转发给目标对象,调用真实对象的方法

public interface UserService {void addUser(String name);
}public class UserServiceImpl implements UserService {@Overridepublic void addUser(String name) {System.out.println("执行 addUser: " + name);}
}public class UserServiceProxy implements UserService {private UserService target;public UserServiceProxy(UserService target) {this.target = target;}@Overridepublic void addUser(String name) {System.out.println("代理:前置操作");target.addUser(name); // 调用真实对象的方法System.out.println("代理:后置操作");}
}

实际执行的还是 UserServiceImpl 的 addUser 方法。

动态代理

jdk动态代理会生成一个代理类方法,这个代理类对象的每次方法调用都会转到InvocationHandler.invoke() 方法里
invoke() 里可以决定:
【1】要不要调用真实对象的方法(有目标对象的情况)
【2】或者自己“模拟实现”(没有目标对象的情况,比如拼 SQL、发 HTTP 请求等)

模拟处理举例:

UserService userService = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),new Class[]{UserService.class},(proxy, method, args) -> {System.out.println("代理执行方法: " + method.getName());if ("addUser".equals(method.getName())) {System.out.println("模拟插入数据库: " + args[0]);}return null;}
);
userService.addUser("Tom");

这里 addUser 没有真实实现类,但代理在 invoke 里写了逻辑,所以看起来就像方法执行了。

示例

public interface MathCalculator {int div(int i, int j);int mul(int i, int j);int sub(int i, int j);int add(int i, int j);}
@Component
public class MathCalculatorImpl implements MathCalculator {@Overridepublic int div(int i, int j) {return 0;}@Overridepublic int mul(int i, int j) {return 0;}@Overridepublic int sub(int i, int j) {return 0;}@Overridepublic int add(int i, int j) {return i+j;}
}
@Testvoid test01() {// JDK 动态代理 (JDK Dynamic Proxy)MathCalculatorImpl target = new MathCalculatorImpl();InvocationHandler h= new InvocationHandler() {/** proxy:代理对象* method:准备调用的目标对象的这个方法* args:参数*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//目标方法执行Object result = method.invoke(target, args);return result;}};/** ClassLoader loader:类加载器(目标对象)* Class<?>[] interfaces:目标对象实现的接口类型* InvocationHandler h:执行Handler*/MathCalculator proxyInstance = (MathCalculator)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), h);int add=proxyInstance.add(1, 2);System.out.println(add);}

解释:
对 proxyInstance.add 的调用会被代理对象拦截。这种拦截会触发提供的 InvocationHandler 的 invoke 方法

方法调用被拦截: 当在 proxyInstance 上调用 add(1, 2) 时,代理对象本身并没有 add 方法的实现。它会将这个调用导向 InvocationHandler 的 invoke 方法。

invoke 方法执行: invoke 方法随即被执行。传递给它的参数是:

proxy:代理对象本身。

method:一个 Method 对象,代表 add(int, int) 方法。

args:一个 Object 数组,包含参数 {1, 2}。

目标方法被调用: 在 invoke 方法内部,有一行代码 Object result = method.invoke(target, args);。这才是真正神奇的地方。正在显式地使用 method 对象(它代表 add 方法)和 target 对象(MathCalculatorImpl)来调用实际实现类中的 add 方法。
也就是说:这里虽然不是像静态代理那样直接调用,但是触发到InvocationHandler的invoke方法里面具体操作是使用反射来调用目标对象的add方法

简而言之,代理将调用委托给了 InvocationHandler 的 invoke 方法,而 InvocationHandler 的职责是决定如何处理这个调用,在代码中,它的决定就是将调用转发给真正的 target 对象。【这里也是区别于静态代理的,直接调用目标对象的方法是静态代理的典型操作】

总结

Java动态代理的经典实现,它的核心思想就是:

1.方法拦截:

当你调用 proxyInstance.add(1, 2) 时,这个调用并不会直接进入 MathCalculatorImpl 类【具体实现类】。Proxy 对象会拦截这个调用,并将其重定向到 InvocationHandler 的 invoke 方法。

2.反射调用

在 invoke 方法内部,我们可以只写一些逻辑,看起来像方法调用了,这里当然就没有进行反射操作了。
但是我们也可以像Object result = method.invoke(target, args);这样,使用反射来调用目标对象的对应方法。
这里的 method 参数是一个 java.lang.reflect.Method 对象,它代表了你最初调用的 add 方法。method.invoke(target, args) 正是通过反射机制,在运行时动态地调用了 target 对象 (MathCalculatorImpl 的实例) 上的 add 方法,并将参数 args 传递进去。
所以,虽然 proxyInstance 本身没有直接实现 add 方法,但它通过 InvocationHandler 和反射,最终还是调用了 MathCalculatorImpl 【实现类】的 add 方法

这就是动态代理与静态代理最大的不同。静态代理是编译时就写好的,直接调用目标对象的方法;而动态代理则是在运行时生成代理对象,并通过反射来间接调用目标对象的方法。

http://www.dtcms.com/a/336713.html

相关文章:

  • CF2121B Above the Clouds
  • 【Java】多线程Thread类
  • 什么是AIGC(人工智能生成内容)
  • 牛客周赛 Round 104(小红的树不动点)
  • 人工智能入门②:AI基础知识(下)
  • 计算机程序编程软件开发设计之node..js语言开发的基于Vue框架的选课管理系统的设计与实现、基于express框架的在线选课系统的设计与实现
  • STM32——软硬件I2C
  • Font Awesome Kit 使用详解
  • OTA升级
  • Vue Router 嵌套路由与布局系统详解:从新手到精通
  • 【牛客刷题】随机加减操作:两种高效解法详解(DFS记忆化搜索和动态规划集合更新法)
  • java序列化和反序列化
  • FX10/20 (CYUSB401X)开发笔记5 固件架构
  • 【个人项目】跑者天地—测试报告
  • 深入解析二维数组传参的本质
  • 运动场和光流-动手学计算机视觉17
  • 正则表达式实用面试题与代码解析专栏
  • 【Nginx】限流设置
  • 二三层交换转发业务~基础汇总
  • Mysql笔记-错误条件\处理程序
  • SSM从入门到实践:1.1 Spring框架概述与IoC容器入门
  • 堆(Heap):高效的优先级队列实现
  • duiLib 解决点击标题栏中按钮无响应问题
  • ROS2基础
  • C语言零基础第19讲:自定义类型—联合体和枚举
  • 解锁Java开发神器:XXL-Job从入门到精通
  • BT_LE_ADV_CONN_ONE_TIME 参数详解
  • Spring 创建 Bean 的 8 种主要方式
  • [创业之路-556]:创新的本质是赚不确定性带来的潜在价值,把不确定性逐步转化确定性,周而复始。
  • 产品设计.Ai产品经理