Mockito,Java 9 和 java.lang.ClassNotFoundException:sun.reflect.ReflectionFactory

Mockito, Java 9 and java.lang.ClassNotFoundException: sun.reflect.ReflectionFactory

我的项目是一个使用 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...

我尝试了各种选项和组合:

总是同样的错误。我没主意了。

--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

我认为问题可能在于您使用的是 Java 9 的早期版本。在某些早期访问版本中,访问 sun.reflect 库时出现问题。尝试将 Java 9 的版本更新为 9-ea+115 之后的更高版本,如 this answer 中针对类似(尽管不同)问题的建议。

通过更多挖掘,我在 https://developer.jboss.org/thread/278334 which pointed me to https://docs.jboss.org/author/display/WFLY10/Class+Loading+in+WildFly 找到了解决方案。这篇文章有一个标题为 "Accessing JDK classes" 的部分,其中指出默认情况下并非所有 类 都可用于部署,您需要将它们添加到 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 但有新的错误)

从 Java 11、WildFly 14+ 和 Mockito 2.23.0+ 开始,将 jdk.unsupported 模块依赖项添加到 WAR 可以解决问题。您可以通过两种方式执行此操作:

一个。 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");
}

乙。 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>