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

 更新时间:2025年12月19日 09:12:30   作者:Full Stack Developme  
该文章介绍了如何使用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;
    }

    @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'}
===========================

高级改进

  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;
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 实现逻辑与FieldChangeTracker类似
        // ...
    }
    
    // 其他方法与FieldChangeTracker相同
}

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

到此这篇关于Java使用动态代理和反射实现字段变更跟踪的方案的文章就介绍到这了,更多相关Java动态代理和反射跟踪字段变更内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 一步步教你把SpringBoot项目打包成Docker镜像

    一步步教你把SpringBoot项目打包成Docker镜像

    Docker可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化,下面这篇文章主要给大家介绍了关于SpringBoot项目打包成Docker镜像的相关资料,需要的朋友可以参考下
    2023-02-02
  • java实现字符串和数字转换工具

    java实现字符串和数字转换工具

    这篇文章主要为大家详细介绍了java实现字符串和数字转换工具,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-04-04
  • Java多线程(单例模式,阻塞队列,定时器,线程池)详解

    Java多线程(单例模式,阻塞队列,定时器,线程池)详解

    本文是多线程初级入门,主要介绍了多线程单例模式、阻塞队列、定时器、线程池、多线程面试考点,感兴趣的小伙伴可以跟随小编一起了解一下
    2022-09-09
  • 使用SpringBoot+EasyExcel+Vue实现excel表格的导入和导出详解

    使用SpringBoot+EasyExcel+Vue实现excel表格的导入和导出详解

    这篇文章主要介绍了使用SpringBoot+VUE+EasyExcel 整合导入导出数据的过程详解,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-08-08
  • spring cloud学习入门之config配置教程

    spring cloud学习入门之config配置教程

    这篇文章主要给大家介绍了关于spring cloud学习入门之config配置的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用spring cloud具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-09-09
  • Java 中的语法糖,真甜

    Java 中的语法糖,真甜

    语法糖(Syntactic sugar),也叫做糖衣语法,是英国科学家发明的一个术语,通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会.这篇文章主要介绍了Java 中的语法糖知识,需要的朋友可以参考下
    2020-12-12
  • Java邮件发送超时时间过长问题的优化方案

    Java邮件发送超时时间过长问题的优化方案

    邮件发送是许多Java应用中常见的功能,尤其在用户注册、密码重置和系统通知中,对于一个高并发的系统来说,邮件发送的超时问题可能导致应用性能的下降,甚至影响用户体验,因此,本期我们将讨论Java邮件发送超时时间过长 的问题,并深入探讨其成因和优化策略
    2025-06-06
  • springboot的EnvironmentPostProcessor接口方法源码解析

    springboot的EnvironmentPostProcessor接口方法源码解析

    这篇文章主要介绍了springboot的EnvironmentPostProcessor接口方法源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • SpringBoot2.x中management.security.enabled=false无效的解决

    SpringBoot2.x中management.security.enabled=false无效的解决

    这篇文章主要介绍了SpringBoot2.x中management.security.enabled=false无效的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • java 枚举类定义静态valueOf(java.lang.String)方法的问题及解决

    java 枚举类定义静态valueOf(java.lang.String)方法的问题及解决

    这篇文章主要介绍了java 枚举类定义静态valueOf(java.lang.String)方法的问题及解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09

最新评论