如何使用ByteBuddy创建动态代理 [英] How to create a dynamic proxy using ByteBuddy
问题描述
在Java中,可以使用 InvocationHandler
的实现来创建动态代理.尽管JVM进行了优化,但使用反射总是会产生调用方法的一些开销.
In Java it is possible to create dynamic proxies using an implementation of InvocationHandler
. Despite JVM optimizations, using reflection will always have some overhead invoking a method.
为尝试解决此问题,我尝试使用ByteBuddy在运行时创建代理类,但是文档在这方面似乎不够清楚.
To try to solve this problem, I tried to use ByteBuddy to create the proxy classes at runtime, but the documentation didn't seem clear enough on this aspect.
如何创建 MethodCallProxy
以便将方法调用转发到某些类实例?
How do I create a MethodCallProxy
in order to forward a method invocation to some class instance?
为了更好地阐明我的问题,我提供了一个我要实现的示例:
To better clarify my problem, I am providing an example of what I want to achieve:
我正在构建一个RPC系统.在方法调用的每一侧,我都有一个定义合同的接口(当两个调用方/被调用方都在JVM下运行时).
I am building an RPC system. On each side of a method invocation, I have an interface defining the contract (when both caller/callee are running under the JVM).
@Contract
interface ISomeService {
fun someMethod(arg0: String, arg1: SomePojo): PojoResult
}
在调用站点,我注入了一个代理,该代理可以拦截所有方法调用并将其转发给被调用者.
At the call site, I inject a proxy that intercepts all method calls and forwards them to the callee.
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>
最后,在被调用方,我有几个处理程序,每个处理程序一个处理程序,负责解组调用参数并将其转发给服务实现.
And, finally, at the callee, I have several handlers, one for each service method, responsible for unmarshalling the invocation parameters and forwarding them to the service implementation.
@Service
class SomeServiceImpl {
fun someMethod(arg0: String, arg1: SomePojo): PojoResult {
// ...
}
}
我可以使用代码生成来解决此问题,但是生成的 jar
文件可能会变得很大.因此,我想创建这些处理程序的通用版本,并在每个实例中附加一个代理,以拦截对 ISomeService
的每个方法调用,并将其转发给 SomeServiceImpl
.>
I could solve this problem using code generation, but the resulting jar
file can become very big. Thus, I want to create a generic version of these handlers and, in each instance, attach a proxy that intercepts every method call to ISomeService
and forwards them to SomeServiceImpl
.
推荐答案
在Byte Buddy中有多种创建代理类的方法.确切的方法取决于您的用例.最简单的方法可能是使用 InvocationHandlerAdapter
.假设您要为 SomeClass
创建代理,则可以使用以下命令创建一个代理:
There are many ways of creating proxy classes in Byte Buddy. The exact way depends on your use-case. The easiest way might be to use the InvocationHandlerAdapter
. Given that you want to create a proxy for SomeClass
, you can create one using:
Class<? extends SomeClass> proxy = new ByteBuddy()
.subclass(SomeClass.class)
.method(ElementMatchers.any())
.intercept(InvocationHandlerAdapter.of(invocationHandler))
.make()
.load(SomeClass.class.getClassLoader());
如果要创建一个具有到不同实例的委托的代理,则还需要定义一个字段.可以按照以下说明进行操作:
If you want to create a proxy with a delegate to different instance, you would additionally define a field. This can be done by the following instructions:
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接口(例如)来设置上述字段:
You would set the above field via reflection or by implementing a setter interface such as for example:
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());
您现在可以实例化该类并将其强制转换为用于设置处理程序的接口.
You can now instantiate the class and cast the class to the interface for setting the handler.
除了 InvocationHandler
外,还有许多其他创建代理的方法.一种方法是使用 MethodDelegation
,它更灵活,通常更快,并且允许您按需调用超级方法.也可以使用 MethodCall
或 Forwarding
工具应用转发工具.您可以在相应的类javadoc中找到详细信息.
Beyond the InvocationHandler
, there are many other ways to create a proxy. One way would be using MethodDelegation
which is more flexible, often faster and allows you to invoke a super method on demand. A forwarding insrumentation can also be applied using a MethodCall
or a Forwarding
instrumentation. You can find detailed information in the respective classes javadoc.
这篇关于如何使用ByteBuddy创建动态代理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!