我认为它与此有关:Do security providers cause ClassLoader leaks in Java?。基本上Security
是重新使用旧的类加载器的提供者。所以这可能发生在任何多类路径环境(如OSGi)中,而不仅仅是SBT。
修复您build.sbt
(无分叉):
testOptions in Test += Tests.Cleanup(() =>
java.security.Security.removeProvider("BC"))
实验:
sbt-classloader-issue$ sbt
> test
[success] Total time: 1 s, completed Jul 6, 2017 11:43:53 PM
> test
[success] Total time: 0 s, completed Jul 6, 2017 11:43:55 PM
说明:
,我可以从你的代码中看到(发表here):
Security.addProvider(new BouncyCastleProvider)
您每次运行测试时都重复使用相同的BouncyCastleProvider
提供程序,因为您的Security.addProvider
工程only first time。由于sbt为每次“测试”运行创建了新的类加载器,但重新使用相同的JVM - Security
是JVM-bootstrap加载的JVM范围的单例,因此classOf[java.security.Security].getClassLoader() == null
和sbt无法重新加载/重新初始化此类。
而且你可以方便地查看
classOf[org.bouncycastle.jce.spec.ECParameterSpec].getClassLoader()
res30: ClassLoader = URLClassLoader with NativeCopyLoader with RawResources
org.bouncycastle
类加载自定义类加载(从SBT),它改变了你运行test
的每次。
所以这个代码:
val generator = KeyPairGenerator.getInstance("ECDSA", "BC")
会从旧的类加载器(一个用于第一次“测试”运行)加载的类的实例,你想与新的类加载器的规格将其初始化:
generator.initialize(ecSpec)
这就是为什么你会得到“参数对象不是ECParameterSpec”异常。 “net.i2p.crypto.eddsa.EdDSAPublicKey不能转换为net.i2p.crypto.eddsa.EdDSAPublicKey”的推理基本相同。
你能否在你的问题中包含重现此问题的代码?为什么你认为类加载器是问题? –
我最终为sbt写了一个问题报告来尝试和调试这个,请查看https://github.com/sbt/sbt/issues/3306我写了一个小项目来重现这个问题 – simao