Java 使用动态代理和反射实现字段变更跟踪
下面我将展示如何使用Java动态代理和反射技术来跟踪记录对象字段的变更前和变更后的数据。
实现方案
我们将创建一个FieldChangeTracker
代理,它能够:
在字段被修改前记录原始值
在字段被修改后记录新值
将所有变更记录保存在日志中
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'}
===========================
高级改进
支持非接口类:可以使用CGLIB库来代理没有接口的类
线程安全:为changeHistory添加同步控制
过滤敏感字段:添加注解标记不需要跟踪的字段
持久化存储:将变更记录保存到数据库或文件
性能优化:缓存反射获取的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相同
}
这种实现方式可以跟踪任何类的字段变更,而不仅限于实现了接口的类。