6

我尝试使用 Byte Buddy 拦截对方法的调用和对 Java 8 lambda 表达式的调用,AgentBuilder如下所示:

static {
  final Instrumentation inst = ByteBuddyAgent.install();
  new AgentBuilder.Default()
        .type(ElementMatchers.nameContainsIgnoreCase("foo"))
        .transform((builder, typeDescription) ->
                builder.method(ElementMatchers.any())
                        .intercept(MethodDelegation.to(LogInterceptor.class)))
        .installOn(inst);
}

public static class LogInterceptor {
  @RuntimeType
  public static Object log(@SuperCall Callable<?> superCall) throws Exception {
    System.out.println("yeah...");
    return superCall.call();
  }
}

我正在使用字节好友 v0.7.1。

它可以拦截以下Runnable(匿名类):

FunnyFramework.callMeLater(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello from inner class");
    }
});

当然还有对定义为普通(非匿名)类的对象的任何调用。但拦截不适用于 lambda 表达式,例如:

FunnyFramework.callMeLater(() -> {
    System.out.println("Hello from lambda");
});

我怎样才能拦截 lambda 表达式调用?据我所知,Byte Buddy 中没有LambdaInterceptor这样的东西。

4

1 回答 1

7

Java 虚拟机不允许转换表示 lambda 表达式的类文件。表示 lambda 表达式的类由继承另一个类的安全上下文的所谓匿名类加载器(不要与经典匿名类混淆)加载,例如,一个加载有匿名类加载器的类,该类将加载的类绑定到另一个类Foo可以private访问Foo. 此加载使用sun.misc.UnsafeAPI 显式发生。

Byte Buddy 挂钩到Java 检测 API,它允许ClassFileTransformers 的应用程序挂钩到ClassLoaders 加载过程。由于匿名类加载器在常识中不被视为ClassLoaders,因此检测 API 不允许此类检测,因此禁止检测 lambda 表达式。

这对于某些用例来说当然是不幸的,但在大多数实际应用程序中,并没有真正需要检测 lambda 表达式。例如,许多现实世界的工具被应用于使用给定注释注释的方法,这不可能应用于 lambda 表达式或比功能接口更复杂的类。


更新:使用 Byte Buddy 1.1.0 版,可以检测表示 lambda 表达式的类。为此,Byte Buddy 检测 JVMLambdaMetafactory并用自定义定义替换类生成。要激活此功能,请在构建器中执行以下步骤:

new AgentBuilder.Default()
  .with(LambdaInstrumentationStrategy.ENABLED)

请注意,这仅适用于 OpenJDK 8u40,在以前的版本中,存在与 invokedynamic 调用站点相关的错误,该错误阻止了此操作。

于 2015-11-25T08:59:25.420 回答