Maven如何将Jar依赖直接嵌入另一个Jar?(不解压依赖类)
在Maven中构建内嵌Jar的可执行Jar(保留原始Jar完整性)
问题背景
需要构建一个内嵌多个第三方Jar的可执行Jar,但第三方厂商Jar不能被解压(会破坏签名导致功能失效)。而Maven的maven-shade-plugin和maven-assembly-plugin默认会提取依赖Jar中的类合并到主Jar中,不符合需求。想知道如何在Maven中实现直接嵌入Jar的效果,是否必须使用maven-ant-run-plugin?
以下是需要迁移到Maven CI/CD流水线的等效Ant构建目标:
<target name="create_cli_jar" depends="clean,init,compile,build_properties" description="Build and package the xyz command line utility"> <jar destfile="${dist.dir}/${cli.prefix}.jar"> <manifest> <attribute name="Main-Class" value="org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader"/> <attribute name="Rsrc-Main-Class" value="com.xyz.GuiCliMain"/> <attribute name="Class-Path" value="."/> <attribute name="Rsrc-Class-Path" value="./ commons-net-3.6.jar commons-io-2.6.jar CodeMeter.jar"/> </manifest> <fileset dir="${dist.dir}" includes="build.properties" /> <fileset dir="${classes.dir}"/> <zipfileset src="lib/jar-in-jar-loader.zip"/> <zipfileset dir="lib" includes="commons-net-3.6.jar"/> <zipfileset dir="lib" includes="commons-io-2.6.jar"/> <zipfileset dir="lib" includes="CodeMeter.jar"/> </jar> </target>
解决方案
不需要局限于maven-ant-run-plugin,有两种可行方案:
方案1:复用现有Ant逻辑(maven-ant-run-plugin)
这是最直接的方式,直接在Maven中调用成熟的Ant构建逻辑:
- 在
pom.xml中配置maven-ant-run-plugin,绑定到package阶段:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-ant-run-plugin</artifactId> <version>3.1.0</version> <executions> <execution> <phase>package</phase> <goals> <goal>run</goal> </goals> <configuration> <target> <!-- 替换Ant变量为Maven内置变量 --> <jar destfile="${project.build.directory}/${project.artifactId}-${project.version}.jar"> <manifest> <attribute name="Main-Class" value="org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader"/> <attribute name="Rsrc-Main-Class" value="com.xyz.GuiCliMain"/> <attribute name="Class-Path" value="."/> <attribute name="Rsrc-Class-Path" value="./ commons-net-3.6.jar commons-io-2.6.jar CodeMeter.jar"/> </manifest> <fileset dir="${project.build.directory}" includes="build.properties" /> <fileset dir="${project.build.outputDirectory}"/> <zipfileset src="${project.basedir}/lib/jar-in-jar-loader.zip"/> <zipfileset dir="${project.basedir}/lib" includes="commons-net-3.6.jar"/> <zipfileset dir="${project.basedir}/lib" includes="commons-io-2.6.jar"/> <zipfileset dir="${project.basedir}/lib" includes="CodeMeter.jar"/> </jar> </target> </configuration> </execution> </executions> </plugin>
注:${dist.dir}对应Maven的${project.build.directory},${classes.dir}对应${project.build.outputDirectory}。
方案2:纯Maven插件实现(无需Ant)
通过maven-jar-plugin配置自定义Manifest,配合maven-dependency-plugin复制依赖Jar后打包进主Jar:
- 配置
maven-dependency-plugin复制依赖到构建目录:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>3.6.0</version> <executions> <execution> <id>copy-dependencies</id> <phase>prepare-package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/lib</outputDirectory> <!-- 仅复制需要内嵌的指定依赖 --> <includeArtifactIds>commons-net,commons-io</includeArtifactIds> <includeGroupIds>commons-net,commons-io</includeGroupIds> <!-- 厂商Jar(如CodeMeter.jar)可手动放入该目录,或配置为system scope依赖 --> </configuration> </execution> </executions> </plugin>
- 配置
maven-jar-plugin设置Manifest并打包内嵌资源:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.3.0</version> <configuration> <archive> <manifest> <mainClass>org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader</mainClass> <addClasspath>true</addClasspath> <classpathPrefix>./</classpathPrefix> </manifest> <manifestEntries> <Rsrc-Main-Class>com.xyz.GuiCliMain</Rsrc-Main-Class> <Rsrc-Class-Path>./ commons-net-3.6.jar commons-io-2.6.jar CodeMeter.jar</Rsrc-Class-Path> <Class-Path>.</Class-Path> </manifestEntries> </archive> <!-- 打包项目类、依赖Jar、加载器及配置文件 --> <additionalResources> <resource> <directory>${project.build.directory}/lib</directory> <includes> <include>*.jar</include> </includes> </resource> <resource> <directory>${project.basedir}/lib</directory> <includes> <include>jar-in-jar-loader.zip</include> </includes> </resource> <resource> <directory>${project.build.directory}</directory> <includes> <include>build.properties</include> </includes> </resource> </additionalResources> </configuration> </plugin>
说明:
jar-in-jar-loader.zip需提前放在项目lib目录,它的内容会被解压到主Jar中,提供嵌套Jar的加载能力。- 厂商Jar需保持完整签名,直接打包即可,不要修改。
关键注意点
- 必须使用Jar-in-Jar Loader(如Eclipse的实现),因为JVM默认不支持从Jar中加载嵌套Jar,需要自定义类加载器。
- 确保内嵌的厂商Jar未被修改,签名完整。
内容的提问来源于stack exchange,提问作者simgineer
相关产品推荐
相关产品推荐

