使用Byte Buddy拦截对Java 8 lambda表达式的调用

时间:2015-11-25 08:51:29

标签: java bytecode instrumentation byte-buddy

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

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();
  }
}

我正在使用Byte Buddy 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 这样的东西。

1 个答案:

答案 0 :(得分:7)

Java虚拟机不允许转换表示lambda表达式的类文件。表示lambda表达式的类由所谓的anonymous class loaders加载(不要与传统的anonymous classes混淆)继承另一个类的安全上下文,例如:一个加载了匿名类加载器的类,它将加载的类绑定到另一个类Foo,可以访问private的{​​{1}}方法。此加载使用Foo API明确发生。

Byte Buddy挂钩到Java instrumentation API,允许应用sun.misc.Unsafe s挂钩到ClassFileTransformer的加载过程。由于匿名类加载器在常识中不被视为ClassLoader,因此检测API不允许进行此类检测,因此禁止对lambda表达式进行检测。

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

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

LambdaMetafactory

请注意,这仅适用于OpenJDK 8u40,在以前的版本中,存在与invokedynamic调用站点相关的错误,导致无法正常工作。