如何基于Byte Buddy根据Invocation动态调用多接口服务实例的方法?
问题
我有如下接口:
public interface DatabaseClient { void save(Entity entity, Consumer<Long> callback); void load(long id, Consumer<Entity> callback); }
以及该接口的实现类DatabaseClientImpl。
我用Byte Buddy创建了DatabaseClient接口的子类,生成的类会把每个方法委托给一个创建Invocation记录的类,该记录定义如下:
public record Invocation(Method method, Object[] parameters) {}
Invocation对象随后会被传递给实现了java.util.function.Consumer<Invocation>的抽象类ProxyHandler。现在我需要实现ProxyHandler的子类,根据Invocation中存储的方法与参数,调用DatabaseClientImpl实例的对应方法。
以下是我目前的实现代码:
public <T> void register(Class<T> intf, T service) throws ReflectiveOperationException { ProxyHandler proxyHandler = new ByteBuddy() .subclass(ProxyHandler.class) .method(isAbstract()) // todo intercept and dynamically call method //.intercept(MethodCall.invoke(loadMethod) // .on(service) // .with(1L, callback)) .make() .load(ProxyHandler.class.getClassLoader()) .getLoaded() .newInstance(); proxyHandler.accept(new Invocation(...)); } public static abstract class ProxyHandler implements Consumer<Invocation> { }
用MethodCall的静态调用方式仅适用于测试场景,由于存在DatabaseClient之外的其他接口,这种静态方案无法满足需求。
请问是否存在基于Invocation的属性,将调用委托给DatabaseClientImpl的可行方案?
解决方案
当然可以实现通用的动态委托,核心思路是在生成的ProxyHandler子类中,利用Invocation携带的Method和参数,通过反射调用目标服务实例的对应方法,完全不需要静态绑定特定方法。
具体实现方案
- 给生成的
ProxyHandler子类添加字段存储目标服务实例,通过带参构造方法注入,保证适配任意接口的服务。 - 拦截
accept方法,将调用逻辑委托给一个处理类,在该类中完成基于Invocation的反射调用。
完整代码示例
import net.bytebuddy.ByteBuddy; import net.bytebuddy.implementation.FieldAccessor; import net.bytebuddy.implementation.MethodCall; import net.bytebuddy.implementation.MethodDelegation; import net.bytebuddy.matcher.ElementMatchers; import java.lang.reflect.Method; import java.util.function.Consumer; public class ProxyHandlerFactory { public <T> void register(Class<T> intf, T service) throws ReflectiveOperationException { // 生成ProxyHandler子类并注入目标服务 ProxyHandler proxyHandler = new ByteBuddy() .subclass(ProxyHandler.class) // 添加私有字段存储目标服务实例 .defineField("targetService", Object.class, java.lang.reflect.Modifier.PRIVATE) // 定义带参构造方法,用于传入目标服务 .defineConstructor(java.lang.reflect.Modifier.PUBLIC) .withParameter(Object.class) .intercept(MethodCall.invoke(Object.class.getConstructor()) .andThen(FieldAccessor.ofField("targetService").setsArgumentAt(0))) // 拦截accept方法,委托给InvocationHandler处理 .method(ElementMatchers.named("accept").and(ElementMatchers.takesArguments(Invocation.class))) .intercept(MethodDelegation.to(new InvocationHandler(service))) .make() .load(ProxyHandler.class.getClassLoader()) .getLoaded() // 通过带参构造方法传入目标服务实例 .getConstructor(Object.class) .newInstance(service); // 测试示例:构造Invocation并调用 Method loadMethod = DatabaseClient.class.getMethod("load", long.class, Consumer.class); proxyHandler.accept(new Invocation(loadMethod, new Object[]{1L, (Consumer<Entity>) entity -> System.out.println("Loaded entity: " + entity)})); } public static abstract class ProxyHandler implements Consumer<Invocation> {} // 处理Invocation的核心逻辑类 private static class InvocationHandler { private final Object targetService; public InvocationHandler(Object targetService) { this.targetService = targetService; } // 方法签名需与被拦截的accept方法匹配 public void accept(Invocation invocation) throws Throwable { Method targetMethod = invocation.method(); Object[] args = invocation.parameters(); // 反射调用目标服务的对应方法 targetMethod.invoke(targetService, args); } } // 辅助类定义 public static class Entity {} public static class DatabaseClientImpl implements DatabaseClient { @Override public void save(Entity entity, Consumer<Long> callback) { callback.accept(100L); System.out.println("Saved entity, generated ID: 100"); } @Override public void load(long id, Consumer<Entity> callback) { callback.accept(new Entity()); System.out.println("Loaded entity with ID: " + id); } } }
关键说明
- 该方案不局限于
DatabaseClient接口,只要Invocation中的Method与目标服务的方法签名匹配,就能完成动态调用。 - 通过构造方法注入目标服务实例,保证了代理类的通用性和线程安全性(每个代理实例对应一个服务实例)。
- 反射调用直接基于
Invocation的属性,完全实现了动态委托的需求。
内容的提问来源于stack exchange,提问作者user5809448
相关产品推荐
相关产品推荐

