重新定义方法会覆盖以前的重新定义
Redefining a method overrides previous redefinitions
我正在尝试使用以下代码在 Bar
class 中重新定义 2 个方法:
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();
}
其中 CustomInterceptor
class 如下:
static class CustomInterceptor {
public static String intercept() {
return "Hello!";
}
}
在我的测试中,我执行以下操作来重新定义 Bar#sayHello()
和 Bar#sayHelloAgain()
方法:
@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!");
}
请注意,我明确想要一个一个地替换方法。
测试失败是因为hello
变量是null
(也就是Bar
class中Bar#sayHello()
方法返回的值),但是helloAgain
变量设置为 Hello!
,正如预期的那样(使用了 CustomInterceptor
class)。所以看起来第一个方法重定义在执行第二个时被删除了。
你知道发生了什么事吗?我怎样才能在不丢失第一个方法的情况下保留两个方法的重新定义?
这完全取决于您使用的ClassFileLocator
。您正在使用查询 class 加载器的 class 文件定位器。 class 加载程序总是 return 加载最初可从 class 文件获得的 class 文件,并且不知道任何转换。
如果您想保留第一次转换的更改,您需要在 class 文件定位器中构建某种形式的内存以 return 已更改的 class 文件。您可以从 dynamicType.getBytes()
.
读取转换生成的字节
您还可以使用 ClassFileLocator.AgentBased
的 Java 工具 API 查找实时 class 文件,但这需要附加 Java 代理。
我正在尝试使用以下代码在 Bar
class 中重新定义 2 个方法:
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();
}
其中 CustomInterceptor
class 如下:
static class CustomInterceptor {
public static String intercept() {
return "Hello!";
}
}
在我的测试中,我执行以下操作来重新定义 Bar#sayHello()
和 Bar#sayHelloAgain()
方法:
@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!");
}
请注意,我明确想要一个一个地替换方法。
测试失败是因为hello
变量是null
(也就是Bar
class中Bar#sayHello()
方法返回的值),但是helloAgain
变量设置为 Hello!
,正如预期的那样(使用了 CustomInterceptor
class)。所以看起来第一个方法重定义在执行第二个时被删除了。
你知道发生了什么事吗?我怎样才能在不丢失第一个方法的情况下保留两个方法的重新定义?
这完全取决于您使用的ClassFileLocator
。您正在使用查询 class 加载器的 class 文件定位器。 class 加载程序总是 return 加载最初可从 class 文件获得的 class 文件,并且不知道任何转换。
如果您想保留第一次转换的更改,您需要在 class 文件定位器中构建某种形式的内存以 return 已更改的 class 文件。您可以从 dynamicType.getBytes()
.
您还可以使用 ClassFileLocator.AgentBased
的 Java 工具 API 查找实时 class 文件,但这需要附加 Java 代理。