Reputation: 16238
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?
Upvotes: 1
Views: 924
Reputation: 44067
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.
Upvotes: 1
Reputation: 16238
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")
.)
Upvotes: 1