如何使用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提供了原生的方法委托机制,可以直接调用父类原始方法,完全避开递归,同时也能轻松获取当前拦截的方法信息。
修改步骤:
- 移除
InvocationHandler实现类,改用ByteBuddy的MethodDelegation做方法拦截 - 用ByteBuddy的注解直接获取当前方法和父类方法调用逻辑
- 重构追踪逻辑到独立处理器类中
修改后的代码:
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
相关产品推荐
相关产品推荐

