Intercepting calls to Java 8 lambda-expressions using Byte Buddy

时光总嘲笑我的痴心妄想 提交于 2019-12-03 13:31:15

问题


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:


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.



来源:https://stackoverflow.com/questions/33912026/intercepting-calls-to-java-8-lambda-expressions-using-byte-buddy

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!