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 时,它解决了原生库加载的问题,因为这些库需要在物理文件系统上解压才能被操作系统访问和加载。