Macos上一切正常
打包发布到服务器报错:
java.lang.RuntimeException: java.nio.file.NoSuchFileException: /BOOT-INF/lib/ortools-linux-x86-64-9.9.3963.jar!/ortools-linux-x86-64/
at com.google.ortools.Loader.loadNativeLibraries(Loader.java:134)
...
org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175)
at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:47)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88)
at com.guazi.cars.task.appconfig.RestControllerAspect.around(RestControllerAspect.java:49)
at sun.reflect.GeneratedMethodAccessor340.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
覆盖Loader,添加输出,发现读取的是依赖jar中的c++实现:
resourceURI:jar:file:/app/ortools-test-1.0-SNAPSHOT.jar!/ortools-linux-x86-64/
进入服务器容器内,查看打包后的文件:
# jar tf cars-task.jar
META-INF/ META-INF/MANIFEST.MF BOOT-INF/lib/commons-validator-1.6.jar BOOT-INF/lib/commons-digester-1.8.1.jarBOOT-INF/lib/jna-5.14.0.jar BOOT-INF/lib/ortools-java-9.9.3963.jar BOOT-INF/lib/ortools-darwin-x86-64-9.9.3963.jar BOOT-INF/lib/ortools-win32-x86-64-9.9.3963.jar BOOT-INF/lib/ortools-linux-aarch64-9.9.3963.jar BOOT-INF/lib/ortools-darwin-aarch64-9.9.3963.jar BOOT-INF/lib/jna-platform-4.5.2.jar BOOT-INF/lib/ortools-linux-x86-64-9.9.3963.jar
到这已经不会了,打包也打了这个依赖,但是读不到,
后来通过这个讨论找到了解决方案:https://github.com/google/or-tools/discussions/3553
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<requiresUnpack> <!-- 添加这个配置,完美解决 -->
<dependency>
<groupId>com.google.ortools</groupId>
<artifactId>ortools-java</artifactId>
</dependency>
<dependency>
<groupId>com.google.ortools</groupId>
<artifactId>ortools-linux-x86-64</artifactId>
</dependency>
<dependency>
<groupId>com.google.ortools</groupId>
<artifactId>ortools-darwin-x86-64</artifactId>
</dependency>
<dependency>
<groupId>com.google.ortools</groupId>
<artifactId>ortools-win32-x86-64</artifactId>
</dependency>
<dependency>
<groupId>com.google.ortools</groupId>
<artifactId>ortools-linux-aarch64</artifactId>
</dependency>
<dependency>
<groupId>com.google.ortools</groupId>
<artifactId>ortools-darwin-aarch64</artifactId>
</dependency>
</requiresUnpack>
</configuration>
</plugin>
Spring Boot 打包和 Executable JARs
Spring Boot 使用一个特殊的打包方式,将应用程序的所有依赖库都打包到一个可执行的 JAR 文件中,这些文件通常位于 BOOT-INF/lib
目录下。这种打包方式很方便,因为它允许你轻松地分发和部署你的应用程序,但它也带来了一些挑战,特别是当涉及到加载原生库(如 OR-Tools 中使用的 .so
文件或 .dll
文件)时。
JNI 和原生库
JNI 库是用其他编程语言(如 C 或 C++)编写的,它们不是标准的 Java 类文件,因此不能被 JVM 直接从 JAR 中加载。JNI 库需要从文件系统中的具体位置加载,而当这些库被封装在 JAR 文件内部时,Java 程序在运行时无法直接访问和加载这些库。
解决方案:requiresUnpack
通过在 spring-boot-maven-plugin
配置中添加 requiresUnpack
,你指示 Spring Boot 在应用程序启动时将指定的依赖解压到临时目录。这样做使得 JNI 库变得“可见”并且可以被操作系统正确加载:
- 解压 :在应用程序启动时,Spring Boot 将
requiresUnpack
中列出的依赖解压到一个临时文件夹。 - 加载 :由于这些文件现在位于文件系统上的一个实际位置,JVM 可以正确加载这些原生库,你的应用程序可以正常使用它们,避免了
NoSuchFileException
。
通过这种方式,Spring Boot 能够克服标凑 JAR 文件的限制,适当地处理和加载包含在 JAR 中的原生库。这是处理包含原生库的 Java 应用程序的常见实践,特别是在使用 Spring Boot 等框架时。
因此,当你在配置中添加 requiresUnpack
时,它解决了原生库加载的问题,因为这些库需要在物理文件系统上解压才能被操作系统访问和加载。
全部评论