如何将存在包冲突的JAR与*.so库打包为可CLASSPATH引用的单JAR
可行,但需要针对性处理两个核心问题
你遇到的情况可以实现单JAR运行,但不能直接用jar命令简单合并,得分别解决JAR包冲突和本地.so库加载两个问题,具体方案如下:
一、解决JAR包冲突:用Shadow JAR重命名冲突包
因为两个JAR存在包冲突,直接解压合并会导致类覆盖,这里可以用Shadow JAR技术,通过构建插件(比如Maven Shade或Gradle Shadow)把其中一个JAR里的冲突包路径重命名,避免类路径冲突。
以Maven为例,在pom.xml中配置Shade插件,指定冲突包的重命名规则:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.4.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <relocations> <!-- 把冲突的包路径从原路径重命名到新路径 --> <relocation> <pattern>com.example.conflict</pattern> <shadedPattern>com.example.shaded.conflict</shadedPattern> </relocation> </relocations> <!-- 指定最终JAR的主类(如果需要直接运行) --> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.yourpackage.CustomLauncher</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin>
插件会自动修改类文件中的引用路径,确保重命名后的包能被正确调用,彻底解决冲突问题。
二、处理本地.so库:打包进JAR并运行时提取
JVM默认无法直接加载JAR包内的本地.so库,所以需要把.so库打包到JAR的资源目录中,再通过自定义启动类在运行时将其提取到临时目录,然后加载。
- 先把.so库放到项目的
src/main/resources/native/目录下,打包时会被自动包含进JAR。 - 编写自定义启动类,负责提取并加载.so库,再调用原第三方软件的主类:
import java.io.File; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.StandardCopyOption; public class CustomLauncher { public static void main(String[] args) throws Exception { // 提取第一个.so库 loadNativeLibrary("libxxx.so"); // 提取第二个.so库 loadNativeLibrary("libyyy.so"); // 调用第三方软件的原主类 com.thirdparty.OriginalMain.main(args); } private static void loadNativeLibrary(String libName) throws Exception { InputStream libStream = CustomLauncher.class.getResourceAsStream("/native/" + libName); if (libStream == null) { throw new RuntimeException("Failed to find native library: " + libName); } // 创建临时文件,程序退出时自动删除 File tempLib = File.createTempFile(libName.replace(".so", ""), ".so"); tempLib.deleteOnExit(); // 复制库文件到临时目录 Files.copy(libStream, tempLib.toPath(), StandardCopyOption.REPLACE_EXISTING); // 设置可执行权限(Linux/macOS下需要) tempLib.setExecutable(true); // 加载本地库 System.load(tempLib.getAbsolutePath()); } }
三、完整打包&运行流程
- 执行构建命令(Maven:
mvn clean package),会生成包含所有重命名JAR和.so库的单JAR文件。 - 直接运行该JAR:
java -jar your-final-jar.jar,不需要额外配置CLASSPATH,所有依赖都会被正确加载。
注意事项
- 如果需要支持多架构(比如x86_64和ARM64),可以把不同架构的.so库放到
native/linux-x86_64/、native/linux-arm64/等子目录,启动时通过System.getProperty("os.arch")判断当前架构,加载对应的库。 - 如果冲突包中存在硬编码的类名(比如反射中的字符串),需要手动修改这些字符串,或者在Shade插件中配置字符串替换规则。
- 临时文件的权限问题:在Linux/macOS下必须设置可执行权限,否则无法加载。
内容的提问来源于stack exchange,提问作者Azii
相关产品推荐
相关产品推荐

