Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I am putting together a very simple ByteBuddy delegate/proxy class.

The intention is (again, very simple) to proxy a class such that any of its non- final , non- private , non- static methods and so forth get routed to equivalent methods on its proxiedInstance field as returned by its getProxiedInstance method. (There should be exceptions made for the usual suspects: equals , hashCode , wait and notify and so on.)

I've set up my proxy class using the subclass strategy. I've also defined two methods, getProxiedInstance and setProxiedInstance , along with a private field named proxiedInstance of the proper type. I've done this using the FieldAccessor.ofBeanProperty() strategy. I've omitted that here for brevity and clarity. The class does in fact contain this field and these two methods.

Then I've defined the method interception like this, statically importing the relevant ElementMatchers methods:

builder
      .method(not(isFinal()).and(not(isStatic())).and(not(isPrivate()))
              .and((isPublic().and(named("toString")).and(takesArguments(0)).and(returns(String.class)))
                   .or((not(isDeclaredBy(Object.class)).and(not(named("getProxiedInstance"))).and(not(named("setProxiedInstance"))))))
      .intercept(MethodDelegation.toMethodReturnOf("getProxiedInstance"));

In English: not final, not static, not private, and either the public String toString() method inherited from Object (or overridden), or any other method not declared by Object.class and not named getProxiedInstance or setProxiedInstance.

Suppose I have a class like this:

public class Frob {
  public String sayHello() {
    return "Hello!";

When I create a proxy class for it, instantiate it, and then call toString() on the proxy, I get Hello!.

This suggests to me somehow that the recipe I've quoted above is somehow routing toString() to sayHello().

From reading the MethodDelegation javadocs, it seems that maybe sayHello on the target/delegate object is picked for delegation because it is more specific than the method invoked on the proxy (toString). I guess name matching is lower priority than that.

I think this use case I have is relatively simple. How do I best accomplish it?

The best I could do, which works, but seems perhaps a little clunky or verbose, was this:

builder = builder
      .method(not(isDeclaredBy(Object.class))
              .and(not(isFinal()))
              .and(not(isStatic()))
              .and(not(isPrivate()))
              .and(not(named("getProxiedInstance")))
              .and(not(named("setProxiedInstance"))))
      .intercept(MethodDelegation.toMethodReturnOf("getProxiedInstance"))
      .method(is(toStringMethod))
      .intercept(invoke(toStringMethod).onMethodCall(invoke(named("getProxiedInstance"))));

(toStringMethod is the Method resulting from Object.class.getMethod("toString").)

I think using MethodCall is a better approach to this. MethodDelegation is meant for "catch all proxies" where you inject corresponding dispatchers into what is often a single delegate method, maybe two. Method call is also much more performance since it does not need to do the analysis but just reroutes to a method of a compatible type.

Thank you for your answer. My understanding is with MethodCall, however, I have to know what method to call. Is there some recipe using the fluent API that I am unaware of that says, "if the user called x, invoke x on the proxiedInstance"? – Laird Nelson Mar 23, 2020 at 16:55 In other words: I don't know what method the user is going to call on the dynamic type. When she calls that method, whatever it is, I want to call an equivalent method on the return value of getProxiedInstance. MethodCall.invoke(ElementMatcher) seems to work on the instrumented type, and I don't see an ElementMatcher that matches "whatever the user called originally". – Laird Nelson Mar 23, 2020 at 17:30 I've tried to do my homework here; github.com/raphw/byte-buddy/blob/master/byte-buddy-dep/src/test/… though shows only usages where the argument to invoke is known in advance. That's not my case. – Laird Nelson Mar 23, 2020 at 18:49

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.