You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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构建逻辑:

  1. 在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:

  1. 配置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>
  1. 配置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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 18:57:05