重新定义方法会覆盖以前的重新定义

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(也就是Barclass中Bar#sayHello()方法返回的值),但是helloAgain 变量设置为 Hello!,正如预期的那样(使用了 CustomInterceptor class)。所以看起来第一个方法重定义在执行第二个时被删除了。

你知道发生了什么事吗?我怎样才能在不丢失第一个方法的情况下保留两个方法的重新定义?

这完全取决于您使用的ClassFileLocator。您正在使用查询 class 加载器的 class 文件定位器。 class 加载程序总是 return 加载最初可从 class 文件获得的 class 文件,并且不知道任何转换。

如果您想保留第一次转换的更改,您需要在 class 文件定位器中构建某种形式的内存以 return 已更改的 class 文件。您可以从 dynamicType.getBytes().

读取转换生成的字节

您还可以使用 ClassFileLocator.AgentBased 的 Java 工具 API 查找实时 class 文件,但这需要附加 Java 代理。