9

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.

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.

How do I create a MethodCallProxy in order to forward a method invocation to some class instance?

Edit:

To better clarify my problem, I am providing an example of what I want to achieve:

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 {
        // ...
    }
}

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.

1 Answer 1

11

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());

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.

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.

Sign up to request clarification or add additional context in comments.

20 Comments

Of course, to address that specific part of the question, there is no reason to assume that a ByteBuddy generated proxy calling an InvocationHandler is more efficient that a JRE generated java.lang.reflect.Proxy calling an InvocationHandler
At runtime, there is no vararg. Simply provide the above method with a single array as an argument but cast it to Object. I could overload the method, but even then, the call would be ambiguous. Let me think how I can improve this!
Maybe, I got it wrong, but I read the OP’s description as having an issue with withAllArguments(), not with the method with(Object...). Further, the varargs nature of a method is detectable at runtime…
@RafaelWinterhalter I see. I didn't know about reflection inflation either. Also, I wasn't aware boxing and unboxing had a sensible overhead. If it ever becomes a problem, I can change the impl to generate the handler bytecode in order to avoid it.
This SO question stackoverflow.com/questions/60317919/… covers the difference between proxy via Reflection InvocationHandler and Byte Buddy MethodDelegation
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.