Intercepting calls to Java 8 lambda-expressions us

2019-03-20 18:02发布

I try to intercept calls to methods and calls to Java 8 lambda expressions using a Byte Buddy AgentBuilder as follows:

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

I'm using Byte Buddy v0.7.1.

It can intercept the following Runnable (anonymous class):

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

and of course any calls to objects defined as normal (non-anonymous) classes. But the interception does not work for lambda expression's like:

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

How can I intercept also the lambda expression calls? There's no such thing as a LambdaInterceptor in Byte Buddy, as far as I know.

1条回答
啃猪蹄的小仙女
2楼-- · 2019-03-20 18:16

The Java virtual machine does not permit a transformation of a class files representing a lambda expression. Classes that represent lambda expressions are loaded by so-called anonymous class loaders (not to be confused with classical anonymous classes) that inherit another class's security context, e.g. a class that is loaded with an anonymous class loader which binds the loaded class to another class Foo can access private methods of Foo. This loading happens explicitly using the sun.misc.Unsafe API.

Byte Buddy hooks into the Java instrumentation API which allows the application of ClassFileTransformers to hook into a ClassLoaders loading process. As anonymous class loaders are not considered ClassLoaders in the common sense, the instrumentation API does not allow for such instrumentations and therefore prohibits the instrumentation of lambda expressions.

This is of course unfortunate for some use cases, but in most real-life applications, there is no real requirement for instrumenting lambda expression. Many real-world instrumentations are for example applied to methods that are annotated with a given annotation what is not possible to apply to lambda expressions or to classes that are more complex than a functional interface.


UPDATE: With Byte Buddy version 1.1.0, it is possible to instrument classes that represent lambda expressions. For this, Byte Buddy instruments the JVM's LambdaMetafactory and replaces the class generation with a custom definition. To activate this feature, execute the following step in the builder:

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

Note that this does only work with OpenJDK 8u40, in previous versions, there is a bug related to invokedynamic call sites that prevents this from working.

查看更多
登录 后发表回答