我尝试拦截对方法的调用,并使用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 这样的东西。
答案 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调用站点相关的错误,导致无法正常工作。