I want to achieve the following scenario using Byte Buddy
A more detailed description of the setup is as follows:
I have classes Base
and A
public abstract class Base {
protected Base() {}
protected Base(String s, Object o) {...}
}
public abstract class A {
protected A() {}
}
I want to add another constructor to A
equivalent to
protected A(String s, Integer i) {
super(s, i);
}
So I wrote
new AgentBuilder.Default().type(named("A"))
.transform((builder, typeDescription, classLoader, module) ->
builder.defineConstructor(Visibility.PROTECTED)
.withParameter(String.class)
.withParameter(Integer.class)
.intercept(
MethodCall.invoke(isConstructor().and(takesArguments(String.class, Object.class)))
.withArgument(0, 1)))
.installOn(instrumentation);
If I run my code with this agent, I am able to see the new constructor added at runtime.
Now I want to reference this newly added constructor in another class B
.
public class B extends A {
// I want to add this method
newMethod() {
super("str", 0)
}
}
So I wrote
new AgentBuilder.Default().type(isSubTypeOf(A.class)))
.transform((builder, typeDescription, classLoader, module) ->
builder.defineMethod("newMethod", typeDescription, Visibility.PUBLIC)
.intercept(
MethodCall.invoke(isConstructor().and(takesArguments(String.class, Integer.class)))
.with("str")
.with(0)))
.installOn(instrumentation);
However, I receive an error like below
java.lang.IllegalStateException: class B does not define exactly one virtual method or constructor for (isConstructor() and hasParameter(hasTypes(erasures(containing(is(class String), is(class Integer)))))) but contained 0 candidates: []
Can I not reference a method which I added in a previous transformer? (First-time ByteBuddy user)
The problem is that the other class needs to be located from the class path where the stored class file is represented in the old format. Instead of using a matcher which implicitly browses the hierarchy, rather supply the method explicitly, for example by using a MethodDescription.Latent
.