Is it possible create new final field on a class and create a constructor with parameter that is set to that final field on class instantiation? I tried several approaches and searched for answers but found no example how to do it.
What I have now:
Class proxyClass = new ByteBuddy().subclass(Object.class).implement((Type[]) interfaces)
.defineField("dispatcherInvocationHandler", ByteBuddyDispatcherInvocationHandler.class, Modifier.PRIVATE + Modifier.FINAL)
.defineConstructor(Modifier.PUBLIC)
.withParameter(ByteBuddyDispatcherInvocationHandler.class)
.intercept(MethodCall.invokeSuper().andThen(/* copy argument to field */))
.method(ElementMatchers.any())
.intercept(InvocationHandlerAdapter.toField("dispatcherInvocationHandler"))
.make()
.load(BytebuddyProxyGenerator.class.getClassLoader())
.getLoaded();
I don't want to make field public and I don't want to create property with setter / getter to that field. I'd like to end up with something like:
public class DontCare {
private final ByteBuddyDispatcherInvocationHandler dispatcherInvocationHandler;
public DontCare(ByteBuddyDispatcherInvocationHandler arg) {
super();
this.dispatcherInvocationHandler = arg;
}
}
Is there any simple way how to do this?
Updated 31.10.2016
Finally I got it working with the help of Rafael and 1.5.2 version of the ByteBuddy library. Working code is this:
Class proxyClass = new ByteBuddy().subclass(Object.class).implement((Type[]) interfaces)
.defineField("dispatcherInvocationHandler", ByteBuddyDispatcherInvocationHandler.class, Modifier.PRIVATE + Modifier.FINAL)
.defineConstructor(Modifier.PUBLIC)
.withParameter(ByteBuddyDispatcherInvocationHandler.class)
.intercept(
MethodCall.invoke(Object.class.getConstructor())
.onSuper()
.andThen(
FieldAccessor.ofField("dispatcherInvocationHandler")
.setsArgumentAt(0)
)
)
.method(ElementMatchers.any())
.intercept(InvocationHandlerAdapter.toField("dispatcherInvocationHandler"))
.make()
.load(ByteBuddyProxyGenerator.class.getClassLoader())
.getLoaded();
Unfortunately, there is no good way in the moment. I did however take this as an inspiration to refactor the FieldAccessor
implementation which now allows you to do the following:
builder.defineField("desiredField",
DispatcherInvocationHandler.class,
Visibility.PRIVATE, FieldManifestation.FINAL)
.defineConstructor(Visibility.PUBLIC)
.withParameters(String.class)
.intercept(MethodCall.invokeSuper() // Given such a constructor exists
.andThen(FieldAccessor.ofField("desiredField").setsArgumentAt(0)))
.make()
.load(BytebuddyProxyGenerator.class.getClassLoader())
.getLoaded();
When you define such an explicit setter, the instrumentation also becomes chainable by itself. You can therefore set multiple fields such as:
FieldAccessor.ofField("foo")
.setsArgumentAt(0)
.andThen(FieldAccessor.ofField("bar").setsArgumentAt(1));
With the above implementation, a constructor (or any method) would be implemented similar to:
class Sample {
Object foo, bar;
Sample(Object a1, Object a2) {
foo = a1;
bar = a2;
}
}
This additional API is released with Byte Buddy 1.5.2.