Mac M1上已用JNA 5.12.1,Testcontainers仍报UnsatisfiedLink错误
我在Mac M1上构建基于sbt的项目,使用Testcontainers的JUnit4测试时出现错误。网上多数方案指出是JNA版本过旧(5.6.0之前)导致,于是我将Testcontainers升级到1.18.1版本(该版本自带JNA 5.12.1),依赖配置如下:
"junit" % "junit" % "4.12" % Test, "org.testcontainers" % "testcontainers" % "1.18.1" % Test, "org.testcontainers" % "mysql" % "1.18.1" % Test
我执行sbt dependencyTree和sbt test:dependencyTree确认没有旧版JNA引入,当前仅使用5.12.1版本,甚至替换了.sbt文件夹中的JNA版本。
使用的Java是通过sdkman!安装的Zulu 11:
openjdk 11.0.18 2023-01-17 LTS OpenJDK Runtime Environment Zulu11.62+17-CA (build 11.0.18+10-LTS) OpenJDK 64-Bit Server VM Zulu11.62+17-CA (build 11.0.18+10-LTS, mixed mode)
现有方案均无效,请问更新至最新库后仍报错的原因是什么?是否有绕过方案(如不使用UnixSocket与Docker通信)?
错误日志如下:
[error] Test com.redacted.my.Test failed: java.lang.UnsatisfiedLinkError: 无法加载库:/Users/myself/Library/Caches/JNA/temp/jna13305103600218314813.tmp, took 0.0 sec [error] at java.lang.ClassLoader.loadLibrary(ClassLoader.java:2633) [error] at java.lang.Runtime.load0(Runtime.java:768) [error] at java.lang.System.load(System.java:1837) [error] at com.sun.jna.Native.loadNativeDispatchLibraryFromClasspath(Native.java:1018) [error] at com.sun.jna.Native.loadNativeDispatchLibrary(Native.java:988) [error] at com.sun.jna.Native.<clinit>(Native.java:195) [error] at com.github.dockerjava.transport.DomainSocket.<clinit>(DomainSocket.java:54) [error] at com.github.dockerjava.transport.UnixSocket.get(UnixSocket.java:29) [error] at org.testcontainers.dockerclient.DockerClientProviderStrategy.lambda$test$2(DockerClientProviderStrategy.java:175) [error] at org.testcontainers.dockerclient.DockerClientProviderStrategy.test(DockerClientProviderStrategy.java:189) [error] at org.testcontainers.dockerclient.DockerClientProviderStrategy.tryOutStrategy(DockerClientProviderStrategy.java:266) [error] at org.testcontainers.dockerclient.DockerClientProviderStrategy.lambda$getFirstValidStrategy$5(DockerClientProviderStrategy.java:245) [error] at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:176) [error] at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177) [error] at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177) [error] at java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1632) [error] at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:127) [error] at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:502) [error] at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:488) [error] at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) [error] at java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150) [error] at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) [error] at java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:543) [error] at org.testcontainers.dockerclient.DockerClientProviderStrategy.getFirstValidStrategy(DockerClientProviderStrategy.java:246) [error] at org.testcontainers.DockerClientFactory.getOrInitializeStrategy(DockerClientFactory.java:150) [error] at org.testcontainers.DockerClientFactory.client(DockerClientFactory.java:186) [error] at org.testcontainers.DockerClientFactory$1.getDockerClient(DockerClientFactory.java:104) [error] at com.github.dockerjava.api.DockerClientDelegate.authConfig(DockerClientDelegate.java:109) [error] at org.testcontainers.containers.GenericContainer.start(GenericContainer.java:321) ...
可能的原因
- JNA本地库架构不匹配:Mac M1是ARM64架构,虽然JNA 5.12.1支持ARM64,但如果JNA下载的本地临时库是x86_64版本(可能是缓存或下载源问题),就会加载失败。你看到的
jnaXXX.tmp文件可能是错误架构的版本。 - Docker Desktop配置问题:Docker Desktop在M1上需要确保是ARM64版本,若运行在x86兼容模式下,可能导致JNA调用Unix Socket时出现架构不兼容。
- JVM启动参数缺失:Zulu 11在M1上运行时,可能需要明确指定参数强制JNA使用自带的本地库,而非系统或缓存中的旧版本。
- 缓存权限问题:JNA缓存目录
/Users/myself/Library/Caches/JNA/的权限异常,导致无法正确写入或读取临时库文件,进而加载失败。
绕过/解决方法
1. 强制使用Docker TCP连接(绕过Unix Socket)
在sbt中设置测试JVM参数,让Testcontainers使用Docker的TCP端口而非Unix Socket:
Test / javaOptions += "-Dtestcontainers.host=localhost" Test / javaOptions += "-Dtestcontainers.docker.socket=tcp://localhost:2375"
注意:需先在Docker Desktop的设置中开启"Expose daemon on tcp://localhost:2375 without TLS"选项(路径:Settings -> General 或 Settings -> Docker Engine)。
2. 清理JNA缓存并重新下载
删除JNA缓存目录,让JNA重新下载适配ARM64的本地库:
rm -rf ~/Library/Caches/JNA/
之后重新运行sbt test即可。
3. 添加JVM参数强制JNA使用正确库
在sbt测试配置中添加以下参数:
Test / javaOptions += "-Djna.nosys=true"
jna.nosys=true会禁止JNA使用系统中的本地库,强制使用依赖包中的版本。
4. 验证Docker Desktop架构
确保Docker Desktop是ARM64版本:
- 打开Docker Desktop -> Settings -> General,确认"Use the new Virtualization framework"已开启;若版本不符,从Docker官网重新下载ARM64安装包。
5. 升级Testcontainers到更高版本
1.18.1之后的版本(如1.19.x及以上)对M1兼容性更好,尝试升级到最新稳定版:
"org.testcontainers" % "testcontainers" % "1.19.7" % Test, "org.testcontainers" % "mysql" % "1.19.7" % Test
内容的提问来源于stack exchange,提问作者jbx

