Skip to content

Instantly share code, notes, and snippets.

@ljnelson
Last active December 21, 2020 18:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ljnelson/0bf33635b8a1404d47d42747045b5960 to your computer and use it in GitHub Desktop.
Save ljnelson/0bf33635b8a1404d47d42747045b5960 to your computer and use it in GitHub Desktop.
A recipe for sketching out ByteBuddy proxy objects
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.MethodCall;
import static net.bytebuddy.implementation.MethodCall.invoke;
import static net.bytebuddy.matcher.ElementMatchers.named;
DynamicType.Builder<?> builder = //... acquire the builder, then:
.defineField("proxiedInstance", theClassBeingProxied, Visibility.PRIVATE) // (1)
.implement(new DefaultParameterizedType(null, Proxy.class, theClassBeingProxied)) // (2)
.intercept(FieldAccessor.ofBeanProperty()) // (3)
.method(someMatcher) // (4)
.intercept(invoke(MethodCall.MethodLocator.ForInstrumentedMethod.INSTANCE) // (5)
.onMethodCall(invoke(named("getProxiedInstance")))
.withAllArguments());
// 1: Adds a field to the proxy class named proxiedInstance. It will hold the "real" object.
// 2: Proxy.class is a made-up interface defining getProxiedInstance()/setProxiedInstance(T),
// where T is the type of the thing being proxied; e.g. Proxy<Frob>.
// DefaultParameterizedType is a made-up implementation of java.lang.reflect.ParameterizedType.
// 3: Magic ByteBuddy incantation to implement the Proxy<Frob> interface by making two methods
// that read from and write to the proxiedInstance field just defined
// 4: Choose what methods to intercept here; see the net.bytebuddy.matcher.ElementMatchers class
// in particular
// 5: The serious magic is here. It means, roughly, "whatever the method the user just called,
// turn around and invoke it on the return value of the getProxiedInstance() method with all
// of the arguments the user originally supplied". That INSTANCE object is not documented
// anywhere, really; you just have to know that it is suitable for use here in this DSL
// "sentence".
@PhBastiani
Copy link

PhBastiani commented Dec 21, 2020

Hello,
To be compliant with all methods, you need to add a call to MethodCall.withAllArguments. This adds all arguments of the instrumented method as arguments to the invoked method...

.intercept(MethodCall.invoke(MethodCall.MethodLocator.ForInstrumentedMethod.INSTANCE)
                                   .onMethodCall(MethodCall.invoke(ElementMatchers.named("getProxiedInstance")))
                                   .withAllArguments())  

@ljnelson
Copy link
Author

Nice catch; thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment