我可以创建一个带有 private static final MethodHandle 字段的 ByteBuddy 检测类型吗?

Can I create a ByteBuddy instrumented type with a private static final MethodHandle field in it?

我过去曾问过 similar question 但无法实现(不清楚是否可行)。

现在我发现自己 需要 创建一个 private static final 字段,其类型(在我的例子中)是 MethodHandle

在Java中,当然,我可以简单地做:

private static final MethodHandle mh = goGetMethodHandle();

…其中 goGetMethodHandle() 是一个 static 函数,returns 我需要 MethodHandle

或者,等价地,我可以这样做:

private static final MethodHandle mh;
static {
  mh = goGetMethodHandle();
}

我不确定 ByteBuddy 的配方应该是什么。

这有效,但把我吓死了:

  // Excerpt from JUnit Jupiter unit test whose class is named
  // TestPrivateStaticFinalFieldInitialization:

  @Test
  final void testAll() throws Throwable {

    final MethodDescription findStaticMethodDescription = new TypeDescription.ForLoadedType(MethodHandles.Lookup.class)
      .getDeclaredMethods()
      .filter(ElementMatchers.named("findStatic"))
      .getOnly();
    
    final MethodDescription methodHandlesLookupMethodDescription = new TypeDescription.ForLoadedType(MethodHandles.class)
      .getDeclaredMethods()
      .filter(ElementMatchers.named("lookup"))
      .getOnly();

    final MethodDescription methodTypeMethodTypeMethodDescription = new TypeDescription.ForLoadedType(MethodType.class)
      .getDeclaredMethods()
      .filter(ElementMatchers.named("methodType")
              .and(ElementMatchers.isStatic()
                   .and(ElementMatchers.takesArguments(Class.class))))
      .getOnly();
    
    final ByteBuddy byteBuddy = new ByteBuddy();
    DynamicType.Builder<?> builder = byteBuddy.subclass(Object.class);
    builder = builder
      .defineField("gorp", MethodHandle.class, Visibility.PRIVATE, Ownership.STATIC, SyntheticState.SYNTHETIC, FieldManifestation.FINAL)
      .invokable(ElementMatchers.isTypeInitializer())
      .intercept(MethodCall.invoke(findStaticMethodDescription)
                 .onMethodCall(MethodCall.invoke(methodHandlesLookupMethodDescription))
                 .with(new TypeDescription.ForLoadedType(TestPrivateStaticFinalFieldInitialization.class))
                 .with("goop")
                 .withMethodCall(MethodCall.invoke(methodTypeMethodTypeMethodDescription)
                                 .with(new TypeDescription.ForLoadedType(void.class)))
                 .setsField(new FieldDescription.Latent(builder.toTypeDescription(),
                                                        "gorp",
                                                        ModifierContributor.Resolver.of(Visibility.PRIVATE,
                                                                                        Ownership.STATIC,
                                                                                        SyntheticState.SYNTHETIC,
                                                                                        FieldManifestation.FINAL).resolve(),
                                                        TypeDescription.Generic.OfNonGenericType.ForLoadedType.of(MethodHandle.class),
                                                        Collections.emptyList())));
    final Class<?> newClass = builder.make().load(Thread.currentThread().getContextClassLoader()).getLoaded();
    final Field gorpField = newClass.getDeclaredField("gorp");
    gorpField.setAccessible(true);
    final MethodHandle methodHandle = (MethodHandle)gorpField.get(null);
    assertNotNull(methodHandle);
    methodHandle.invokeExact();
  }

  public static final void goop() {
    System.out.println("*** goop");
  }

到此结束,您可以在终端上看到*** goop

我在这里做的事情有没有可以简化的?

例如,我创建 FieldDescription.Latent 的方式似乎很疯狂。有没有我遗漏的更短的方式?

使用 DSL 可以更轻松地完成您的解决方案:

builder
  .defineField("gorp", MethodHandle.class, 
     Visibility.PRIVATE, Ownership.STATIC, SyntheticState.SYNTHETIC, FieldManifestation.FINAL)
  .invokeable(isTypeInitializer())
  .intercept(MethodCall.invoke(Some.class.getMethod("goGetMethodHandle")
    .setsField(named("gorp")))