下面我将展示如何使用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;
}
@Override
public 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;
}
@Override
public 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;
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public int getAge() {
return age;
}
@Override
public 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;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 实现逻辑与FieldChangeTracker类似
// ...
}
// 其他方法与FieldChangeTracker相同
}
这种实现方式可以跟踪任何类的字段变更,而不仅限于实现了接口的类。