Mockito、Java 9 和 java.lang.ClassNotFoundException:sun.reflect.ReflectionFactory
Posted
技术标签:
【中文标题】Mockito、Java 9 和 java.lang.ClassNotFoundException:sun.reflect.ReflectionFactory【英文标题】:Mockito, Java 9 and java.lang.ClassNotFoundException: sun.reflect.ReflectionFactory 【发布时间】:2019-01-03 14:38:53 【问题描述】:我的项目是一个使用 Mockito 测试库的 Wildfly 13 应用程序。该应用程序未使用 Java 9 模块结构。只要服务器在 Java 8 上运行,测试就可以正常工作,但是一旦我们升级到 Java 9,它们就会失败,并出现以下异常:
org.objenesis.ObjenesisException: java.lang.ClassNotFoundException: sun.reflect.ReflectionFactory from [Module "test.war" from Service Module Loader]
at test.war//org.objenesis.instantiator.sun.SunReflectionFactoryHelper.getReflectionFactoryClass(SunReflectionFactoryHelper.java:63)
at test.war//org.objenesis.instantiator.sun.SunReflectionFactoryHelper.newConstructorForSerialization(SunReflectionFactoryHelper.java:37)
at test.war//org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.<init>(SunReflectionFactoryInstantiator.java:41)
at test.war//org.objenesis.strategy.StdInstantiatorStrategy.newInstantiatorOf(StdInstantiatorStrategy.java:68)
at test.war//org.objenesis.ObjenesisBase.getInstantiatorOf(ObjenesisBase.java:94)
at test.war//org.objenesis.ObjenesisBase.newInstance(ObjenesisBase.java:73)
at test.war//org.mockito.internal.creation.instance.ObjenesisInstantiator.newInstance(ObjenesisInstantiator.java:19)
at test.war//org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker.createMock(SubclassByteBuddyMockMaker.java:47)
at test.war//org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker.createMock(ByteBuddyMockMaker.java:25)
at test.war//org.mockito.internal.util.MockUtil.createMock(MockUtil.java:35)
at test.war//org.mockito.internal.MockitoCore.mock(MockitoCore.java:68)
at test.war//org.mockito.Mockito.mock(Mockito.java:1895)
at test.war//org.mockito.Mockito.mock(Mockito.java:1804)
at test.war//application code...
我尝试了各种选项和组合:
使用 Java 8、9、10 编译 运行最新版本的 OpenJDK 9 和 10 设置编译器和虚拟机选项 --add-modules jdk.unsupported 和 --add-exports jdk.unsupported/sun.reflect=ALL-UNNAMED"总是同样的错误。我没有想法。
--show-module-resolution
的结果
root jdk.javadoc jrt:/jdk.javadoc
root jdk.jdi jrt:/jdk.jdi
root jdk.jshell jrt:/jdk.jshell
root jdk.sctp jrt:/jdk.sctp
root jdk.xml.dom jrt:/jdk.xml.dom
root jdk.jsobject jrt:/jdk.jsobject
root jdk.unsupported jrt:/jdk.unsupported
root jdk.scripting.nashorn jrt:/jdk.scripting.nashorn
root jdk.httpserver jrt:/jdk.httpserver
root jdk.management jrt:/jdk.management
root jdk.net jrt:/jdk.net
root jdk.security.auth jrt:/jdk.security.auth
root jdk.dynalink jrt:/jdk.dynalink
root java.se jrt:/java.se
root jdk.compiler jrt:/jdk.compiler
root jdk.accessibility jrt:/jdk.accessibility
root jdk.jartool jrt:/jdk.jartool
root jdk.jconsole jrt:/jdk.jconsole
root jdk.attach jrt:/jdk.attach
root jdk.security.jgss jrt:/jdk.security.jgss
jdk.security.jgss requires java.logging jrt:/java.logging
jdk.security.jgss requires java.security.sasl jrt:/java.security.sasl
jdk.security.jgss requires java.security.jgss jrt:/java.security.jgss
jdk.attach requires jdk.internal.jvmstat jrt:/jdk.internal.jvmstat
jdk.jconsole requires jdk.attach jrt:/jdk.attach
jdk.jconsole requires java.rmi jrt:/java.rmi
jdk.jconsole requires jdk.management jrt:/jdk.management
jdk.jconsole requires java.management jrt:/java.management
jdk.jconsole requires jdk.management.agent jrt:/jdk.management.agent
jdk.jconsole requires java.desktop jrt:/java.desktop
jdk.jconsole requires jdk.internal.jvmstat jrt:/jdk.internal.jvmstat
jdk.jconsole requires java.management.rmi jrt:/java.management.rmi
jdk.accessibility requires java.desktop jrt:/java.desktop
jdk.compiler requires java.compiler jrt:/java.compiler
java.se requires java.datatransfer jrt:/java.datatransfer
java.se requires java.compiler jrt:/java.compiler
java.se requires java.scripting jrt:/java.scripting
java.se requires java.desktop jrt:/java.desktop
java.se requires java.security.sasl jrt:/java.security.sasl
java.se requires java.naming jrt:/java.naming
java.se requires java.sql.rowset jrt:/java.sql.rowset
java.se requires java.security.jgss jrt:/java.security.jgss
java.se requires java.sql jrt:/java.sql
java.se requires java.management.rmi jrt:/java.management.rmi
java.se requires java.management jrt:/java.management
java.se requires java.xml.crypto jrt:/java.xml.crypto
java.se requires java.instrument jrt:/java.instrument
java.se requires java.rmi jrt:/java.rmi
java.se requires java.prefs jrt:/java.prefs
java.se requires java.logging jrt:/java.logging
java.se requires java.xml jrt:/java.xml
jdk.dynalink requires java.logging jrt:/java.logging
jdk.security.auth requires java.naming jrt:/java.naming
jdk.security.auth requires java.security.jgss jrt:/java.security.jgss
jdk.management requires java.management jrt:/java.management
jdk.scripting.nashorn requires java.logging jrt:/java.logging
jdk.scripting.nashorn requires java.scripting jrt:/java.scripting
jdk.scripting.nashorn requires jdk.dynalink jrt:/jdk.dynalink
jdk.jsobject requires java.desktop jrt:/java.desktop
jdk.xml.dom requires java.xml jrt:/java.xml
jdk.jshell requires java.compiler jrt:/java.compiler
jdk.jshell requires jdk.compiler jrt:/jdk.compiler
jdk.jshell requires java.logging jrt:/java.logging
jdk.jshell requires jdk.internal.opt jrt:/jdk.internal.opt
jdk.jshell requires jdk.internal.le jrt:/jdk.internal.le
jdk.jshell requires jdk.jdi jrt:/jdk.jdi
jdk.jshell requires java.prefs jrt:/java.prefs
jdk.jshell requires jdk.internal.ed jrt:/jdk.internal.ed
jdk.jdi requires jdk.attach jrt:/jdk.attach
jdk.jdi requires jdk.jdwp.agent jrt:/jdk.jdwp.agent
jdk.javadoc requires java.xml jrt:/java.xml
jdk.javadoc requires java.compiler jrt:/java.compiler
jdk.javadoc requires jdk.compiler jrt:/jdk.compiler
java.security.sasl requires java.logging jrt:/java.logging
java.security.jgss requires java.naming jrt:/java.naming
java.rmi requires java.logging jrt:/java.logging
jdk.management.agent requires java.management jrt:/java.management
jdk.management.agent requires java.management.rmi jrt:/java.management.rmi
java.desktop requires java.xml jrt:/java.xml
java.desktop requires java.prefs jrt:/java.prefs
java.desktop requires java.datatransfer jrt:/java.datatransfer
java.management.rmi requires java.naming jrt:/java.naming
java.management.rmi requires java.management jrt:/java.management
java.management.rmi requires java.rmi jrt:/java.rmi
java.naming requires java.security.sasl jrt:/java.security.sasl
java.sql.rowset requires java.logging jrt:/java.logging
java.sql.rowset requires java.naming jrt:/java.naming
java.sql.rowset requires java.sql jrt:/java.sql
java.sql requires java.logging jrt:/java.logging
java.sql requires java.xml jrt:/java.xml
java.xml.crypto requires java.logging jrt:/java.logging
java.xml.crypto requires java.xml jrt:/java.xml
java.prefs requires java.xml jrt:/java.xml
java.management binds java.management.rmi jrt:/java.management.rmi
java.management binds jdk.management jrt:/jdk.management
jdk.jshell binds jdk.editpad jrt:/jdk.editpad
java.naming binds jdk.naming.dns jrt:/jdk.naming.dns
java.naming binds jdk.naming.rmi jrt:/jdk.naming.rmi
java.datatransfer binds java.desktop jrt:/java.desktop
jdk.dynalink binds jdk.scripting.nashorn jrt:/jdk.scripting.nashorn
java.base binds jdk.charsets jrt:/jdk.charsets
java.base binds java.security.jgss jrt:/java.security.jgss
java.base binds java.security.sasl jrt:/java.security.sasl
java.base binds java.naming jrt:/java.naming
java.base binds jdk.security.jgss jrt:/jdk.security.jgss
java.base binds java.smartcardio jrt:/java.smartcardio
java.base binds jdk.crypto.ec jrt:/jdk.crypto.ec
java.base binds jdk.crypto.cryptoki jrt:/jdk.crypto.cryptoki
java.base binds java.xml.crypto jrt:/java.xml.crypto
java.base binds java.desktop jrt:/java.desktop
java.base binds jdk.localedata jrt:/jdk.localedata
java.base binds jdk.jlink jrt:/jdk.jlink
java.base binds jdk.jdeps jrt:/jdk.jdeps
java.base binds jdk.compiler jrt:/jdk.compiler
java.base binds jdk.jartool jrt:/jdk.jartool
java.base binds jdk.javadoc jrt:/jdk.javadoc
java.base binds jdk.zipfs jrt:/jdk.zipfs
java.base binds java.management jrt:/java.management
java.base binds jdk.security.auth jrt:/jdk.security.auth
java.base binds java.logging jrt:/java.logging
java.compiler binds jdk.compiler jrt:/jdk.compiler
java.compiler binds jdk.javadoc jrt:/jdk.javadoc
jdk.internal.jvmstat binds jdk.jstatd jrt:/jdk.jstatd
java.scripting binds jdk.scripting.nashorn jrt:/jdk.scripting.nashorn
jdk.jstatd requires java.rmi jrt:/java.rmi
jdk.jstatd requires jdk.internal.jvmstat jrt:/jdk.internal.jvmstat
jdk.jdeps requires java.compiler jrt:/java.compiler
jdk.jdeps requires jdk.compiler jrt:/jdk.compiler
jdk.jlink requires jdk.jdeps jrt:/jdk.jdeps
jdk.jlink requires jdk.internal.opt jrt:/jdk.internal.opt
jdk.crypto.cryptoki requires jdk.crypto.ec jrt:/jdk.crypto.ec
jdk.naming.rmi requires java.naming jrt:/java.naming
jdk.naming.rmi requires java.rmi jrt:/java.rmi
jdk.naming.dns requires java.naming jrt:/java.naming
jdk.editpad requires java.desktop jrt:/java.desktop
jdk.editpad requires jdk.internal.ed jrt:/jdk.internal.ed
【问题讨论】:
您使用的是最新版本的 Mockito 吗? @Nicolai 是的。尝试了最新的旧版本,没有变化。 你可以运行 --show-module-resolution 来查看 jdk.unsupported 是否已解决?如果不是,那么 Wildfly 必须使用一些限制可观察性的选项运行。 @AlanBateman 我已经在问题中添加了使用建议的标志运行 Wildfly 的结果。 --show-module-resolution 输出显示 jdk.unsupported 已解决,这意味着它导出的 sun.reflect API 可用。这一定是类加载器委托有问题 - 您添加的答案似乎是确认。 【参考方案1】:从 Java 11、WildFly 14+ 和 Mockito 2.23.0+ 开始,将 jdk.unsupported
模块依赖添加到您的 WAR 可以解决问题。您可以通过两种方式做到这一点:
A. META-INF/MANIFEST.MF
将以下行添加到您的 WAR 存档的 META_INF/MANIFEST.MF
:
Dependencies: jdk.unsupported
注意文件must end with a new line or carriage return。
以下是您通常使用 ShrinkWrap/Arquillian 的方式:
@Deployment
public static WebArchive createDeployment()
return ShrinkWrap.create(WebArchive.class, "my-app.war")
.addPackages(true, Mockito.class.getPackage(), Objenesis.class.getPackage(), ByteBuddy.class.getPackage())
.addAsManifestResource(new StringAsset("Dependencies: jdk.unsupported\n" /* required by Mockito */), "MANIFEST.MF");
B. WEB-INF/jboss-deployment-structure.xml
将以下内容添加到您的 WAR 存档的 WEB-INF/jboss-deployment-structure.xml:
<jboss-deployment-structure>
<deployment>
<dependencies>
<module name="jdk.unsupported" />
</dependencies>
</deployment>
</jboss-deployment-structure>
【讨论】:
感谢您指出 ShrinkWrap/Arquillian 方法,问题已解决。 “EarContent/META-INF/MANIFEST.MF”中的解决方案 A 为我工作 JDK11 + WIldFly20,jboss 序列化库调用 sun/reflect 用于 JEE 耳朵。【参考方案2】:随着更多的挖掘,我在https://developer.jboss.org/thread/278334 找到了解决方案,它指向了https://docs.jboss.org/author/display/WFLY10/Class+Loading+in+WildFly。该文章有一个标题为“访问 JDK 类”的部分,其中指出并非所有类默认都可用于部署,您需要将它们添加到 jboss-deployment-structure.xml
以使其可用。
就我而言:
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.1">
<deployment>
<dependencies>
<system export="true">
<paths>
<path name="sun/reflect"/>
</paths>
</system>
</dependencies>
</deployment>
</jboss-deployment-structure>
此解决方案适用于
Wildfly 13,在 Java 10.0.2 上运行(9.0.4 也可以) 使用 Java 8 编译的应用程序 Mockito 1.10.19(我尝试了 2.20.0 但出现了新错误)【讨论】:
【参考方案3】:我认为问题可能在于您使用的是 Java 9 的早期版本。在一些早期访问版本中,访问 sun.reflect
库时出现问题。尝试按照 this answer 中的建议将您的 Java 9 版本更新为 9-ea+115
之后的更高版本,以解决类似(尽管不同)的问题。
【讨论】:
我使用最新版本的 OpenJDK 9 和 10 进行编译和运行(我用这些信息更新了我的问题)。以上是关于Mockito、Java 9 和 java.lang.ClassNotFoundException:sun.reflect.ReflectionFactory的主要内容,如果未能解决你的问题,请参考以下文章
如何使用mockito和junit为Java中的ExecutorService编写测试用例?
Mockito测试使用java.lang.NullPointerException
Caused by: javax.el.PropertyNotFoundException: Property 'product' not found on type java.lan