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

Java 使用动态代理和反射实现字段变更跟踪

下面我将展示如何使用Java动态代理和反射技术来跟踪记录对象字段的变更前和变更后的数据。

实现方案

我们将创建一个FieldChangeTracker代理,它能够:

  1. 在字段被修改前记录原始值

  2. 在字段被修改后记录新值

  3. 将所有变更记录保存在日志中

1. 创建变更记录数据结构

import java.util.Date;public class FieldChangeRecord {private String fieldName;private Object oldValue;private Object newValue;private Date changeTime;private String methodName;public FieldChangeRecord(String fieldName, Object oldValue, Object newValue, String methodName) {this.fieldName = fieldName;this.oldValue = oldValue;this.newValue = newValue;this.changeTime = new Date();this.methodName = methodName;}@Overridepublic String toString() {return "FieldChangeRecord{" +"fieldName='" + fieldName + '\'' +", oldValue=" + oldValue +", newValue=" + newValue +", changeTime=" + changeTime +", methodName='" + methodName + '\'' +'}';}
}

2. 创建变更跟踪处理器

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;public class FieldChangeTracker implements InvocationHandler {private final Object target;private final List<FieldChangeRecord> changeHistory = new ArrayList<>();public FieldChangeTracker(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 只拦截setter方法if (method.getName().startsWith("set") && args != null && args.length == 1) {// 获取字段名(从setXxx转换为xxx)String fieldName = method.getName().substring(3);fieldName = Character.toLowerCase(fieldName.charAt(0)) + fieldName.substring(1);try {// 获取字段当前值(变更前)Field field = target.getClass().getDeclaredField(fieldName);field.setAccessible(true);Object oldValue = field.get(target);// 调用原始方法修改值Object result = method.invoke(target, args);// 获取字段新值(变更后)Object newValue = field.get(target);// 记录变更changeHistory.add(new FieldChangeRecord(fieldName, oldValue, newValue, method.getName()));return result;} catch (NoSuchFieldException e) {// 如果没有对应字段,直接调用方法return method.invoke(target, args);}} else {// 非setter方法直接调用return method.invoke(target, args);}}public List<FieldChangeRecord> getChangeHistory() {return new ArrayList<>(changeHistory);}public void printChangeHistory() {System.out.println("=== Field Change History ===");for (FieldChangeRecord record : changeHistory) {System.out.println(record);}System.out.println("===========================");}
}

3. 创建代理工厂

import java.lang.reflect.Proxy;public class TrackingProxyFactory {@SuppressWarnings("unchecked")public static <T> T createTrackingProxy(T target, Class<T> interfaceType) {return (T) Proxy.newProxyInstance(interfaceType.getClassLoader(),new Class<?>[]{interfaceType},new FieldChangeTracker(target));}
}

4. 示例使用

public interface User {String getName();void setName(String name);int getAge();void setAge(int age);
}public class UserImpl implements User {private String name;private int age;@Overridepublic String getName() {return name;}@Overridepublic void setName(String name) {this.name = name;}@Overridepublic int getAge() {return age;}@Overridepublic void setAge(int age) {this.age = age;}
}public class Main {public static void main(String[] args) {User realUser = new UserImpl();User userProxy = TrackingProxyFactory.createTrackingProxy(realUser, User.class);userProxy.setName("Alice");userProxy.setAge(25);userProxy.setName("Bob");userProxy.setAge(30);// 获取变更历史FieldChangeTracker tracker = (FieldChangeTracker) Proxy.getInvocationHandler(userProxy);tracker.printChangeHistory();}
}

5. 示例输出

运行上面的Main类后,输出可能如下:

=== Field Change History ===
FieldChangeRecord{fieldName='name', oldValue=null, newValue=Alice, changeTime=..., methodName='setName'}
FieldChangeRecord{fieldName='age', oldValue=0, newValue=25, changeTime=..., methodName='setAge'}
FieldChangeRecord{fieldName='name', oldValue=Alice, newValue=Bob, changeTime=..., methodName='setName'}
FieldChangeRecord{fieldName='age', oldValue=25, newValue=30, changeTime=..., methodName='setAge'}
===========================

高级改进

  1. 支持非接口类:可以使用CGLIB库来代理没有接口的类

  2. 线程安全:为changeHistory添加同步控制

  3. 过滤敏感字段:添加注解标记不需要跟踪的字段

  4. 持久化存储:将变更记录保存到数据库或文件

  5. 性能优化:缓存反射获取的Field对象

CGLIB版本实现

如果需要代理没有实现接口的类,可以使用CGLIB:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;public class CglibTrackingProxyFactory {@SuppressWarnings("unchecked")public static <T> T createTrackingProxy(T target) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(target.getClass());enhancer.setCallback(new CglibFieldChangeTracker(target));return (T) enhancer.create();}
}class CglibFieldChangeTracker implements MethodInterceptor {private final Object target;private final List<FieldChangeRecord> changeHistory = new ArrayList<>();public CglibFieldChangeTracker(Object target) {this.target = target;}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// 实现逻辑与FieldChangeTracker类似// ...}// 其他方法与FieldChangeTracker相同
}

这种实现方式可以跟踪任何类的字段变更,而不仅限于实现了接口的类。

http://www.xdnf.cn/news/1252261.html

相关文章:

  • 生成网站sitemap.xml地图教程
  • 【STM32U385RG 测评】基于VSCode的STM32开发环境搭建
  • 西门子PLC基础指令6:读取时钟指令、设置时钟指令、使能含义与注意
  • 【32】C++实战篇—— m行n列的坐标点,求每行相邻点X差值dX,每列相邻点y差值dY,并以矩阵形式左端对齐
  • JAVA--流程控制语句
  • 【VS + Qt】VS2022 Qt 开发中 ui_xx.h 文件编辑报错但编译正常的问题解决
  • 「iOS」————单例与代理
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘caffe’问题
  • 河南萌新联赛2025第四场-河南大学
  • K8S云原生监控方案Prometheus+grafana
  • yolov1-v3原理解析
  • DHCP 服务器与DNS服务器
  • 服务器——“查询不到显卡驱动,且输入nvidia-smi报错”的解决办法
  • 2.6 sync
  • 媒体资产管理系统和OCR文字识别的结合
  • 多端同步新解法:Joplin+cpolar联合通过开源设计实现跨平台无缝协作?
  • 自动驾驶系统的网络安全风险分析
  • 013 HTTP篇
  • Transwell 细胞迁移与侵袭实验:从原理到操作的详细指南
  • Hive【应用 04】常用DDL操作(数据库操作+创建表+修改表+清空删除表+其他命令)
  • 【android bluetooth 协议分析 03】【蓝牙扫描详解 4】【BR/EDR扫描到设备后如何上报给app侧】
  • Redis中间件(一):Redis相关命令及其原理
  • 企业后端系统常用数据源类型有哪些?
  • 芯片分享【昆泰】——CH7305A -display controller device.
  • Nacos配置中心和数据隔离在idea中的实现
  • Selenium在Pyhton应用
  • 《算法导论》第 8 章—线性时间排序
  • 【C语言】文件操作全解析
  • DevOps时代的知识基座革命:Gitee Wiki如何重构研发协作范式
  • Leetcode题解:739每日温度,用单调栈解决问题!