如何使用 ByteBuddy 创建动态代理
How to create a dynamic proxy using ByteBuddy
在 Java 中,可以使用 InvocationHandler
的实现来创建动态代理。尽管 JVM 进行了优化,但使用反射总是会产生一些调用方法的开销。
为了尝试解决这个问题,我尝试使用 ByteBuddy 在运行时创建代理 classes,但是文档在这方面似乎不够清楚。
如何创建 MethodCallProxy
以便将方法调用转发给某个 class 实例?
编辑:
为了更好地阐明我的问题,我提供了一个我想要实现的示例:
我正在构建一个 RPC 系统。在方法调用的每一侧,我都有一个定义契约的接口(当两个 caller/callee 在 JVM 下都是 运行 时)。
@Contract
interface ISomeService {
fun someMethod(arg0: String, arg1: SomePojo): PojoResult
}
在调用点,我注入了一个代理来拦截所有方法调用并将它们转发给被调用者。
ByteBuddy()
.subclass(Any::class.java)
.implement(serviceClass)
// Service contract method delegation
.method(isDeclaredBy(serviceClass)).intercept(
MethodDelegation
.to(ServiceProxyInterceptor())
.filter(not(isDeclaredBy(Any::class.java)))
)
.make()
.load(this)
.loaded as Class<T>
最后,在被调用方,我有几个处理程序,每个服务方法一个,负责解组调用参数并将它们转发给服务实现。
@Service
class SomeServiceImpl {
fun someMethod(arg0: String, arg1: SomePojo): PojoResult {
// ...
}
}
我可以使用代码生成来解决这个问题,但是生成的 jar
文件可能会变得非常大。因此,我想创建这些处理程序的通用版本,并在每个实例中附加一个代理,该代理拦截对 ISomeService
的每个方法调用并将它们转发给 SomeServiceImpl
.
在 Byte Buddy 中有多种创建代理 classes 的方法。确切的方法取决于您的用例。最简单的方法可能是使用 InvocationHandlerAdapter
。假设您想为 SomeClass
创建一个代理,您可以使用以下方法创建一个:
Class<? extends SomeClass> proxy = new ByteBuddy()
.subclass(SomeClass.class)
.method(ElementMatchers.any())
.intercept(InvocationHandlerAdapter.of(invocationHandler))
.make()
.load(SomeClass.class.getClassLoader());
如果你想创建一个委托给不同实例的代理,你还需要定义一个字段。这可以通过以下说明完成:
Class<? extends SomeClass> proxy = new ByteBuddy()
.subclass(SomeClass.class)
.defineField("handler", InvocationHandler.class, Visibility.PUBLIC)
.method(ElementMatchers.any())
.intercept(InvocationHandlerAdapter.toField("handler"))
.make()
.load(SomeClass.class.getClassLoader());
您可以通过反射或实现 setter 接口来设置上述字段,例如:
interface HandlerSetter {
InvocationHandler getHandler();
void setHandler(InvocationHandler handler);
}
Class<? extends SomeClass> proxy = new ByteBuddy()
.subclass(SomeClass.class)
.defineField("handler", InvocationHandler.class, Visibility.PUBLIC)
.implement(HandlerSetter.class)
.intercept(FieldAccessor.ofField("handler"))
.method(ElementMatchers.any())
.intercept(InvocationHandlerAdapter.toField("handler"))
.make()
.load(SomeClass.class.getClassLoader());
您现在可以实例化 class 并将 class 转换为用于设置处理程序的接口。
除了 InvocationHandler
之外,还有许多其他方法可以创建代理。一种方法是使用 MethodDelegation
,它更灵活,通常更快,并且允许您按需调用超级方法。也可以使用 MethodCall
或 Forwarding
检测来应用转发检测。您可以在各自的 classes javadoc 中找到详细信息。
在 Java 中,可以使用 InvocationHandler
的实现来创建动态代理。尽管 JVM 进行了优化,但使用反射总是会产生一些调用方法的开销。
为了尝试解决这个问题,我尝试使用 ByteBuddy 在运行时创建代理 classes,但是文档在这方面似乎不够清楚。
如何创建 MethodCallProxy
以便将方法调用转发给某个 class 实例?
编辑:
为了更好地阐明我的问题,我提供了一个我想要实现的示例:
我正在构建一个 RPC 系统。在方法调用的每一侧,我都有一个定义契约的接口(当两个 caller/callee 在 JVM 下都是 运行 时)。
@Contract
interface ISomeService {
fun someMethod(arg0: String, arg1: SomePojo): PojoResult
}
在调用点,我注入了一个代理来拦截所有方法调用并将它们转发给被调用者。
ByteBuddy()
.subclass(Any::class.java)
.implement(serviceClass)
// Service contract method delegation
.method(isDeclaredBy(serviceClass)).intercept(
MethodDelegation
.to(ServiceProxyInterceptor())
.filter(not(isDeclaredBy(Any::class.java)))
)
.make()
.load(this)
.loaded as Class<T>
最后,在被调用方,我有几个处理程序,每个服务方法一个,负责解组调用参数并将它们转发给服务实现。
@Service
class SomeServiceImpl {
fun someMethod(arg0: String, arg1: SomePojo): PojoResult {
// ...
}
}
我可以使用代码生成来解决这个问题,但是生成的 jar
文件可能会变得非常大。因此,我想创建这些处理程序的通用版本,并在每个实例中附加一个代理,该代理拦截对 ISomeService
的每个方法调用并将它们转发给 SomeServiceImpl
.
在 Byte Buddy 中有多种创建代理 classes 的方法。确切的方法取决于您的用例。最简单的方法可能是使用 InvocationHandlerAdapter
。假设您想为 SomeClass
创建一个代理,您可以使用以下方法创建一个:
Class<? extends SomeClass> proxy = new ByteBuddy()
.subclass(SomeClass.class)
.method(ElementMatchers.any())
.intercept(InvocationHandlerAdapter.of(invocationHandler))
.make()
.load(SomeClass.class.getClassLoader());
如果你想创建一个委托给不同实例的代理,你还需要定义一个字段。这可以通过以下说明完成:
Class<? extends SomeClass> proxy = new ByteBuddy()
.subclass(SomeClass.class)
.defineField("handler", InvocationHandler.class, Visibility.PUBLIC)
.method(ElementMatchers.any())
.intercept(InvocationHandlerAdapter.toField("handler"))
.make()
.load(SomeClass.class.getClassLoader());
您可以通过反射或实现 setter 接口来设置上述字段,例如:
interface HandlerSetter {
InvocationHandler getHandler();
void setHandler(InvocationHandler handler);
}
Class<? extends SomeClass> proxy = new ByteBuddy()
.subclass(SomeClass.class)
.defineField("handler", InvocationHandler.class, Visibility.PUBLIC)
.implement(HandlerSetter.class)
.intercept(FieldAccessor.ofField("handler"))
.method(ElementMatchers.any())
.intercept(InvocationHandlerAdapter.toField("handler"))
.make()
.load(SomeClass.class.getClassLoader());
您现在可以实例化 class 并将 class 转换为用于设置处理程序的接口。
除了 InvocationHandler
之外,还有许多其他方法可以创建代理。一种方法是使用 MethodDelegation
,它更灵活,通常更快,并且允许您按需调用超级方法。也可以使用 MethodCall
或 Forwarding
检测来应用转发检测。您可以在各自的 classes javadoc 中找到详细信息。