当前位置: 首页 > 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.dtcms.com/a/317329.html

相关文章:

  • 一种基于潜在表征的轻量级无人机热成像超分辨率网络
  • Linux systemd 系统管理:systemctl 控制服务与守护进程
  • Redis集群核心原理与实战解析
  • Pytest项目_day04(Python做接口请求)
  • PyTorch生成式人工智能(26)——使用PyTorch构建GPT模型
  • 语言模型的多个agent
  • Java学习第一百一十部分——CI/CD
  • 输电线路防外破声光预警装置 | 防山火/防钓鱼/防施工安全警示系统
  • vue中reactive()和ref()的用法
  • FluentUI的介绍与使用案列
  • 组合期权:股票担保策略
  • Suno API V5模型 python源码 —— 使用灵感模式进行出创作
  • 从原理图到PCB的布局
  • 优选算法1
  • 学习资料推荐
  • 商用音乐素材获取:素材平台、AI制作与版权考量
  • 如何将照片从POCO手机传输到Mac电脑
  • OpenAI GPT-OSS:首个可在笔记本上运行的推理模型
  • 科技云报到:Agent应用爆发,谁成为向上托举的力量?
  • 微算法科技(NASDAQ:MLGO)利用鸽群分散算法,提高区块链交易匹配算法效能
  • 【博客系统UI自动化测试报告】
  • 【递归完全搜索】USACO Bronze 2019 December - 奶牛排列Livestock Lineup
  • 每日算法刷题Day57:8.6:leetcode 单调栈6道题,用时2h
  • 【前端开发】五. ES5和ES6对比
  • Android 之 Kotlin中的符号
  • OpenObserve非sql模式 query editor 中 xx like ‘|’报错如何处理
  • RNN梯度爆炸/消失的杀手锏——LSTM与GRU
  • Disruptor 的原理、应用场景
  • jspdf或react-to-pdf等pdf报错解决办法
  • iOS混淆工具有哪些?在集成第三方 SDK 时的混淆策略与工具建议