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.
Lambda expressions are a new and important feature included in Java SE 8. They provide a clear and concise way to represent one method interface using an expression. Lambda expressions also improve the Collection libraries making it easier to iterate through, filter, and extract data from a Collection .
For Lambda expressions, the compiler doesn't translate them into something which is already understood by JVM. Lambda syntax that is written by the developer is desugared into JVM level instructions generated during compilation, which means the actual responsibility of constructing lambda is deferred to runtime.
Runnable is a functional interface, that's why we can use lambda expression to create it's instance. Since run() method takes no argument, our lambda expression also have no argument.
In Java 8, using lambda expression and Stream API we can pass processing logic of elements into methods provided by collections and now collection is responsible for parallel processing of elements and not the client. Also, parallel processing effectively utilizes multicore CPUs used nowadays.
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 ClassFileTransformer
s to hook into a ClassLoader
s loading process. As anonymous class loaders are not considered ClassLoader
s 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With