如何使用ByteBuddy创建动态代理 [英] How to create a dynamic proxy using ByteBuddy

查看:282
本文介绍了如何使用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆