You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用ByteBuddy追踪对象变更并解决递归调用问题

问题

我想实现类似工作单元(Unit of Work)的变更追踪逻辑,针对特定生成代码做追踪。我写了下面的ByteBuddy代码:

private Class<? extends Group> getTrackedClass(GroupChangeTrackingProxy trackingProxy) {
    return new ByteBuddy()
            .subclass(Group.class)
            .method(ElementMatchers.isSetter())
            .intercept(InvocationHandlerAdapter.of(new GroupChangeTrackingProxy()))
            .make()
            .load(CustomerPlatformGroupsRepositoryImpl.class.getClassLoader())
            .getLoaded();
}
public class GroupChangeTrackingProxy implements InvocationHandler {

    private final Logger log = LogManager.getLogger(GroupChangeTrackingProxy.class);
    private final GroupModifications target = new GroupModifications();
    private final AtomicBoolean tracking = new AtomicBoolean(false);

    public GroupModifications getTarget() {
        return target;
    }

    public void setTrack(boolean bool) {
        this.tracking.setPlain(bool);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {

        if (tracking.get()) {
            var match = ReflectionUtils.findMethod(GroupModifications.class, method.getName());
            if (match != null) {
                ReflectionUtils.invokeMethod(match, target, args);
                log.debug(
                        "tracking: '{}.{}({})'",
                        () -> proxy.getClass().getSimpleName(),
                        method::getName,
                        () -> args);
            }
        }
// RECURSIVE!!!!
        ReflectionUtils.invokeMethod(method, proxy, args); 

        return null; // should be void methods anyways
    }
}
private Group track(Group value) {
    var tracker = new GroupChangeTrackingProxy();
    var clazz = getTrackedClass(tracker);

    var tracked = createInstanceOf(clazz);
    this.copier.copy(value, tracked);
    tracker.setTrack(true);
    return tracked;
}

当前实现存在递归调用问题,我可以弃用Spring的ReflectionUtils和InvocationHandler,但不知道怎么在ByteBuddy中获取方法名,请问该如何修改以避免递归问题?

解决方案

递归问题的核心是你在InvocationHandler的invoke方法里,又通过反射调用了代理对象的目标方法,这会再次触发代理拦截,形成无限递归。ByteBuddy提供了原生的方法委托机制,可以直接调用父类原始方法,完全避开递归,同时也能轻松获取当前拦截的方法信息。

修改步骤:

  1. 移除InvocationHandler实现类,改用ByteBuddy的MethodDelegation做方法拦截
  2. 用ByteBuddy的注解直接获取当前方法和父类方法调用逻辑
  3. 重构追踪逻辑到独立处理器类中

修改后的代码:

1. 追踪处理器类(替代原GroupChangeTrackingProxy)

import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicBoolean;

public class GroupChangeTracker {
    private final Logger log = LoggerFactory.getLogger(GroupChangeTracker.class);
    private final GroupModifications target = new GroupModifications();
    private final AtomicBoolean tracking = new AtomicBoolean(false);

    public GroupModifications getTarget() {
        return target;
    }

    public void setTrack(boolean bool) {
        this.tracking.setPlain(bool);
    }

    // 拦截setter方法,通过ByteBuddy注解获取关键信息
    public void intercept(@Origin Method method, 
                         @SuperCall Runnable superCall, 
                         @AllArguments Object[] args) {
        if (tracking.get()) {
            try {
                // 直接匹配GroupModifications的对应方法
                Method modificationMethod = GroupModifications.class.getMethod(
                        method.getName(), 
                        method.getParameterTypes()
                );
                modificationMethod.invoke(target, args);
                log.debug(
                        "tracking: '{}.{}({})'",
                        method.getDeclaringClass().getSimpleName(),
                        method.getName(),
                        args
                );
            } catch (Exception e) {
                log.error("Failed to track modification for method: {}", method.getName(), e);
            }
        }
        // 调用父类原始方法,不会触发代理拦截,彻底解决递归
        superCall.run();
    }
}

2. 修改代理类生成方法

private Class<? extends Group> getTrackedClass(GroupChangeTracker tracker) {
    return new ByteBuddy()
            .subclass(Group.class)
            .method(ElementMatchers.isSetter())
            // 将setter方法的调用委托给追踪处理器的intercept方法
            .intercept(MethodDelegation.to(tracker))
            .make()
            .load(CustomerPlatformGroupsRepositoryImpl.class.getClassLoader())
            .getLoaded();
}

3. 修改track方法(仅替换处理器类)

private Group track(Group value) {
    var tracker = new GroupChangeTracker();
    var clazz = getTrackedClass(tracker);

    var tracked = createInstanceOf(clazz);
    this.copier.copy(value, tracked);
    tracker.setTrack(true);
    return tracked;
}

关键说明:

  • @Origin注解:直接获取当前被拦截的Method对象,无需手动反射查找,高效且准确
  • @SuperCall注解:获取父类原始方法的调用逻辑,调用superCall.run()会直接执行父类的setter方法,不会触发代理拦截,彻底解决递归问题
  • 移除了Spring的ReflectionUtils和JDK的InvocationHandler,完全基于ByteBuddy原生API实现,逻辑更简洁可靠

内容的提问来源于stack exchange,提问作者xenoterracide

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 02:24:58