问题描述
Java 8似乎生成了表示lambda表达式的类。例如,代码:
Java 8 appears to generate classes to represent lambda expressions. For instance, the code:
Runnable r = app::doStuff;
大致表现为:
// $FF: synthetic class
final class App$$Lambda$1 implements Runnable {
private final App arg$1;
private App$$Lambda$1(App var1) {
this.arg$1 = var1;
}
private static Runnable get$Lambda(App var0) {
return new App$$Lambda$1(var0);
}
public void run() {
this.arg$1.doStuff();
}
}
据我所知,代码是在运行时生成的。现在,假设有人想将代码注入上述类的 run
方法中。到目前为止,实验产生了 NoClassDefFound
和 VerifyError
的混合:
As I understand this, the code is generated at runtime. Now, suppose one wanted to inject code into the run
method of the above class. Experiments thus far yield a mix of NoClassDefFound
and VerifyError
:
java.lang.NoClassDefFoundError: App$$Lambda$2
at App$$Lambda$2/1329552164.run(Unknown Source)
at App.main(App.java:9)
Caused by: java.lang.ClassNotFoundException: App$$Lambda$2
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 2 more
这是针对:
$ java -version
java version "1.8.0_51"
Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, mixed mode)
甚至在将任何新字节码推入课程之前。
This is even before pushing any new bytecode into the class.
这是前者吗pected?闻起来就像一个JDK错误,但我很高兴错了!
Is this expected? Smells like a JDK bug, but I'm happy to be wrong!
这是一个
推荐答案
对我来说,这似乎是JVM中的一个错误。系统类加载器尝试按名称定位转换后的类。但是,lambda表达式通过匿名类加载加载,其中包含以下条件:
To me, this seems like a bug in the JVM. The system class loader attempts to locate the transformed class by its name. However, lambda expressions are loaded via anonymous class loading where the following condition:
clazz.getClassLoader()
.loadClass(clazz.getName().substring(0, clazz.getName().indexOf('/')))
产生 ClassNotFoundException
,导致 NoClassDefError
。该类不被视为真正的类,并且这样的anonyoumous类例如不会在重新转换之外传递给 ClassFileTransformer
。
yields a ClassNotFoundException
resulting in the NoClassDefError
. The class is not considered a real class and such anonyoumous classes are for example not passed to a ClassFileTransformer
outside of a retransform.
总而言之,在处理匿名类时,检测API对我来说有点麻烦。同样, LambdaForm
s 传递给 ClassFileTransformer
但是所有参数都是 classFileBuffer
设置为 null
什么打破了变压器类的合约。
All in all, the instrumentation API feels a bit buggy to me when dealing with anonymous classes. Similarly, LambdaForm
s are passed to ClassFileTransformer
s but with all arguments but the classFileBuffer
set to null
what breaks the transformer class's contract.
对于您的示例,问题似乎是您返回 null
;返回 classFileBuffer
什么是无操作时,问题就消失了。然而,这不是 ClassFileTransformer
建议的,其中返回 null
是建议的方法:
For your example, the problem seems to be that you return null
; the problem goes away when returning the classFileBuffer
what is a no-op. This is however not what the ClassFileTransformer
suggests, where returning null
is the recommended way of doing this:
对我而言,这似乎是HotSpot中的一个错误。您应该将此问题报告给OpenJDK。
To me, this seems like a bug in HotSpot. You should report this issue to the OpenJDK.
总而言之,完全可以检测匿名加载的类,因为我在我的代码操作库中演示。与普通仪器相比,它需要一些不幸的调整,但运行时支持它。以下是在库中成功运行单元测试的示例:
All in all, it is perfectly possible to instrument anonymously loaded classes as I demonstrate in my code manipulation library Byte Buddy. It requires some unfortunate tweaks compared to normal instrumentation but the runtime supports it. Here is an example that successfully runs as a unit test within the library:
Callable<String> lambda = () -> "foo";
Instrumentation instrumentation = ByteBuddyAgent.install();
ClassReloadingStrategy classReloadingStrategy = ClassReloadingStrategy.of(instrumentation)
.preregistered(lambda.getClass());
ClassFileLocator classFileLocator = ClassFileLocator.AgentBased.of(instrumentation,
lambda.getClass());
assertThat(lambda.call(), is("foo"));
new ByteBuddy()
.redefine(lambda.getClass(), classFileLocator)
.method(named("call"))
.intercept(FixedValue.value("bar"))
.make()
.load(lambda.getClass().getClassLoader(), classReloadingStrategy);
assertThat(lambda.call(), is("bar"));
这篇关于在Java 8中转换lambdas的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!