Reputation: 1600
I'm trying to redefine 2 methods in a Bar
class using the following code:
private <T> Class<? extends T> replaceMethodInClass(final Class<T> subclass,
final ClassFileLocator classFileLocator, final String methodName) {
final Builder<? extends T> classBuilder =
this.bytebuddy.redefine(subclass, classFileLocator);
return classBuilder
.method(ElementMatchers.named(methodName))
.intercept(MethodDelegation.to(CustomInterceptor.class))
.make()
.load(ByteBuddyReplaceMethodInClassTest.class.getClassLoader(),
ClassReloadingStrategy.fromInstalledAgent())
.getLoaded();
}
where the CustomInterceptor
class is as follow:
static class CustomInterceptor {
public static String intercept() {
return "Hello!";
}
}
In my test, I do the following to redefine the Bar#sayHello()
and Bar#sayHelloAgain()
methods:
@Test
public void shouldReplaceTwoMethodsFromClass_instanciateAfterChanges()
throws InstantiationException, IllegalAccessException, Exception {
// given
replaceMethodInClass(Bar.class, ClassFileLocator.ForClassLoader.of(Bar.class.getClassLoader()),
"sayHello");
replaceMethodInClass(Bar.class, ClassFileLocator.ForClassLoader.of(Bar.class.getClassLoader()),
"sayHelloAgain");
// when
final Bar instance = Bar.class.newInstance();
final String hello = instance.sayHello();
final String helloAgain = instance.sayHelloAgain();
// then
assertThat(hello).isEqualTo("Hello!");
assertThat(helloAgain).isEqualTo("Hello!");
}
Note that I explicitly want to replace the methods one by one.
The test fails because the hello
variable is null
(that's the value returned by the Bar#sayHello()
method in the Bar
class), but the helloAgain
variable is set to Hello!
, as expected (the CustomInterceptor
class was used). So it seems like the first method redefinition was erased when doing the second one.
Do you have any idea what happened and how I can keep the 2 method redefinitions while not loosing the first one ?
Upvotes: 2
Views: 92
Reputation: 44032
It all depends on the ClassFileLocator
you are using. You are using a class file locator that is querying a class loader. A class loader always returns the class file that was originally available from the class file and is not aware of any transformations.
If you want to retain the changes of your first transformation, you would need to build some form of memory into your class file locator to return the changed class file. You can read the bytes generated by a transformation from dynamicType.getBytes()
.
You can also look up a live class file using the Java instrumentation API by ClassFileLocator.AgentBased
which does however require attaching a Java agent.
Upvotes: 1