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

设计模式--动态代理

动态代理是 Java 中一种常见的设计模式,它允许在运行时创建一个实现一组接口的代理类对象。Java 提供了 java.lang.reflect 包来支持动态代理的实现。在 JDK 中,可以使用 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口来创建动态代理。

下面是一个简单的示例,展示如何使用 JDK 的动态代理来代理一个接口。

步骤概述

  1. 定义接口:首先定义一个接口,代理对象将实现这个接口。

  2. 实现 InvocationHandler 接口:创建一个类实现 InvocationHandler 接口,该接口包含一个方法 invoke,在该方法中定义代理对象的行为。

  3. 使用 Proxy 类创建代理对象:使用 Proxy.newProxyInstance 方法来创建代理对象,该方法接受类加载器、要实现的接口数组和 InvocationHandler 实现类的实例。

当涉及到理解 JDK 动态代理中的 InvocationHandler 接口时,我们可以用更通俗易懂的方式来解释它的作用和功能。

示例

假设有一个接口 UserService

public interface UserService {void save(String userName);void delete(String userName);
}

 现在我们实现一个 InvocationHandler 接口的类 UserServiceProxy,用于处理代理对象的方法调用:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class UserServiceProxy implements InvocationHandler {private Object target;public UserServiceProxy(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before invoking " + method.getName());// 执行目标对象的方法Object result = method.invoke(target, args);System.out.println("After invoking " + method.getName());return result;}
}

UserServiceProxy 中,我们实现了 InvocationHandler 接口,并覆写了 invoke 方法来定义代理对象的行为。在这个例子中,我们简单地在方法调用前后输出日志。

接下来,我们可以使用 Proxy.newProxyInstance 方法创建代理对象:

import java.lang.reflect.Proxy;public class Main {public static void main(String[] args) {// 创建真实对象UserService realService = new UserServiceImpl();// 创建代理对象UserService proxy = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),new Class[] { UserService.class },new UserServiceProxy(realService));// 调用代理对象的方法proxy.save("Alice");proxy.delete("Bob");}
}

解释代码

  1. 创建真实对象

UserService realService = new UserServiceImpl();

 这里假设 UserServiceImplUserService 接口的实现类

     2.创建代理对象

UserService proxy = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),new Class[] { UserService.class },new UserServiceProxy(realService)
);
  • Proxy.newProxyInstance 方法创建一个代理对象,需要传入类加载器、要实现的接口数组和 InvocationHandler 实现类的实例。
  • UserService.class.getClassLoader() 获取 UserService 接口的类加载器。
  • new Class[] { UserService.class } 指定代理对象实现的接口,这里只有 UserService
  • new UserServiceProxy(realService)InvocationHandler 的实现类实例,用于处理代理对象的方法调用

调用代理对象的方法

proxy.save("Alice");
proxy.delete("Bob");

这些方法调用会被 UserServiceProxy 中的 invoke 方法拦截,并在方法执行前后输出日志。

注意事项

  • 动态代理只能代理接口,不能代理类。
  • Proxy.newProxyInstance 创建的代理对象会实现指定的接口,并委托 InvocationHandler 处理方法调用。

以上就是一个简单的 JDK 动态代理的示例,展示了如何利用 Proxy 类和 InvocationHandler 接口来实现代理对象的创建和方法拦截。

什么是 InvocationHandler?

在使用 JDK 动态代理时,你需要定义一个接口,这个接口是代理对象和真实对象都要实现的。接着,你需要编写一个类来实现 InvocationHandler 接口。这个实现类承担了代理对象的调用处理逻辑,也就是说,当通过代理对象调用接口方法时,实际上会调用 InvocationHandlerinvoke 方法来处理这个调用。

InvocationHandler 的核心功能

InvocationHandler 接口中定义了一个方法 invoke,它接收三个参数:

  • Object proxy:代理对象本身。
  • Method method:被调用的方法对象。
  • Object[] args:方法调用时传入的参数。

invoke 方法中,你可以编写代码来决定如何处理这个方法调用。通常的情况下,你会根据需要:

  • 执行一些额外的逻辑(例如日志记录、性能监控等)。
  • 将方法调用委托给真实对象的对应方法。
  • 根据条件决定是否拦截、修改方法的调用行为等。
为什么需要 InvocationHandler?

动态代理的核心思想是在运行时生成代理对象,这些代理对象能够拦截对其所代理的接口方法的调用,并且能够自定义处理逻辑。InvocationHandler 提供了这样一个处理入口,让你能够控制代理对象如何处理方法调用,从而实现了动态代理的灵活性和可扩展性。

示例场景

举个例子,假设有一个 UserService 接口,其中有一个 getUserById(int id) 方法。你想要在每次调用这个方法时记录日志。你可以通过 JDK 动态代理来实现这个功能:

  1. 定义一个 UserService 接口。
  2. 编写一个 UserServiceImpl 类实现 UserService 接口,提供实际的方法逻辑。
  3. 编写一个 LogInvocationHandler 类实现 InvocationHandler 接口,在 invoke 方法中添加日志记录逻辑。
  4. 使用 Proxy.newProxyInstance 方法创建代理对象,将 UserService 接口和 LogInvocationHandler 关联起来。

这样,当通过代理对象调用 getUserById 方法时,实际上会先进入 LogInvocationHandlerinvoke 方法,你可以在这里编写代码来记录日志,然后再将调用委托给 UserServiceImpl 的相应方法。

相关文章:

  • 2024年,收付通申请开通流程
  • C++数据格式化5 - uint转换成十六进制字符串二进制的data打印成十六进制字符串
  • kotlin之foreach跳出循环
  • GitLab项目组相关操作(创建项目组Group、创建项目组的项目、为项目添加成员并赋予权限)
  • element-plus的Tour 漫游式引导怎么去绑定Cascader 级联选择器或者它的内容 popper
  • flink学习-容错机制
  • PyMuPDF 操作手册 - 01 从PDF中提取文本
  • el-date-picker 有效时间精确到时分秒 且给有效时间添加标记
  • Ubuntu 22.04 下 CURL(C++) 实现分块上传/下载文件源码
  • 学习笔记——交通安全分析05
  • leetcode45 跳跃游戏II
  • 使用Python进行音频处理
  • k8s学习笔记(一)
  • 【AI】消融实验ablation study
  • Zookeeper 集群节点故障剔除、切换、恢复原理
  • [iOS]Core Data浅析一 -- 启用Core Data
  • 「前端早读君006」移动开发必备:那些玩转H5的小技巧
  • 【翻译】babel对TC39装饰器草案的实现
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • Angular 响应式表单 基础例子
  • Apache的80端口被占用以及访问时报错403
  • es6(二):字符串的扩展
  • Java|序列化异常StreamCorruptedException的解决方法
  • JavaScript标准库系列——Math对象和Date对象(二)
  • JavaScript实现分页效果
  • k8s如何管理Pod
  • Object.assign方法不能实现深复制
  • SQLServer之创建显式事务
  • Transformer-XL: Unleashing the Potential of Attention Models
  • vuex 笔记整理
  • 从 Android Sample ApiDemos 中学习 android.animation API 的用法
  • 工作手记之html2canvas使用概述
  • ------- 计算机网络基础
  • 讲清楚之javascript作用域
  • 前端性能优化--懒加载和预加载
  • 区块链将重新定义世界
  • 驱动程序原理
  • 我是如何设计 Upload 上传组件的
  • - 语言经验 - 《c++的高性能内存管理库tcmalloc和jemalloc》
  • 原生 js 实现移动端 Touch 滑动反弹
  • 翻译 | The Principles of OOD 面向对象设计原则
  • ​人工智能之父图灵诞辰纪念日,一起来看最受读者欢迎的AI技术好书
  • ​软考-高级-信息系统项目管理师教程 第四版【第19章-配置与变更管理-思维导图】​
  • ​数据结构之初始二叉树(3)
  • ​一帧图像的Android之旅 :应用的首个绘制请求
  • $(this) 和 this 关键字在 jQuery 中有何不同?
  • $Django python中使用redis, django中使用(封装了),redis开启事务(管道)
  • (13)[Xamarin.Android] 不同分辨率下的图片使用概论
  • (2015)JS ES6 必知的十个 特性
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (Matalb时序预测)PSO-BP粒子群算法优化BP神经网络的多维时序回归预测
  • (ros//EnvironmentVariables)ros环境变量
  • (二)Eureka服务搭建,服务注册,服务发现
  • (二)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (附源码)计算机毕业设计SSM在线影视购票系统