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

Ant打包含同名文件的多依赖库:如何保留所有文件数据?

解决Ant打包依赖Jar时同名资源文件被覆盖的问题

我完全懂你的困扰——多个依赖Jar里带着同名但内容不同的properties文件,直接用zipgroupfileset合并Jar时,后面的文件会把前面的覆盖掉,而Java类加载器默认只会挑类路径里第一个找到的同名资源,这就导致部分功能因为读不到正确的配置直接歇菜了。

下面给你两种可行的解决方案,你可以根据项目情况选:

方案一:重命名同名资源文件(修改打包逻辑)

这个思路很直接:在合并依赖Jar之前,给每个Jar里的同名properties文件加上原Jar的名称前缀,这样所有文件都能完整保留在最终的sdk.jar里,之后只要调整应用的资源加载逻辑,去对应加载带前缀的文件就行。

具体Ant脚本修改步骤

  1. 先确保你已经引入了ant-contrib任务(如果没装,得先下载并加到Ant的类路径里),因为我们要用到<foreach>遍历每个依赖Jar。
  2. 替换你原有package-to-2-jars目标里打包依赖的部分,改成下面这样:
<target name="package-to-2-jars" depends="jar">
    <property name="store.jar.name" value="main"/>
    <property name="store.dir" value="store"/>
    <property name="store.jar" value="${store.dir}/${store.jar.name}.jar"/>
    <property name="storelibs.jar" value="${store.dir}/sdk.jar"/>
    <property name="temp.lib.dir" value="${store.dir}/temp_libs"/> <!-- 新增临时目录存放处理后的文件 -->

    <echo message="Packaging main classes and libraries into two separate JARs at ${store.jar}"/>
    <delete dir="${store.dir}"/>
    <mkdir dir="${store.dir}"/>
    <mkdir dir="${temp.lib.dir}"/>

    <!-- 遍历每个依赖Jar,解压并重命名同名properties -->
    <foreach target="process-single-lib" param="lib.file">
        <fileset dir="lib" includes="*.jar" excludes="*fonts*.jar"/>
    </foreach>

    <!-- 把处理好的文件打包成sdk.jar -->
    <jar destfile="${storelibs.jar}" filesetmanifest="skip">
        <fileset dir="${temp.lib.dir}"/>
        <exclude name="META-INF/*.SF, META-INF/*.DSA, META-INF/*.RSA"/>
    </jar>

    <!-- 原有主Jar打包逻辑不变 -->
    <jar destfile="${store.dir}/temp_final.jar" filesetmanifest="skip">
        <zipgroupfileset dir="dist" includes="*.jar"/>
        <manifest>
            <attribute name="Main-Class" value="${main.class}"/>
        </manifest>
    </jar>

    <zip destfile="${store.jar}">
        <zipfileset src="${store.dir}/temp_final.jar" excludes="META-INF/*.SF, META-INF/*.DSA, META-INF/*.RSA"/>
    </zip>

    <!-- 清理临时文件 -->
    <delete file="${store.dir}/temp_final.jar"/>
    <delete dir="${temp.lib.dir}"/>
</target>

<!-- 新增处理单个依赖Jar的目标 -->
<target name="process-single-lib">
    <basename property="lib.name" file="${lib.file}" suffix=".jar"/>
    <mkdir dir="${temp.lib.dir}/${lib.name}"/>
    <!-- 解压当前Jar到临时子目录 -->
    <unzip src="${lib.file}" dest="${temp.lib.dir}/${lib.name}"/>
    <!-- 给所有properties文件加上Jar名称前缀 -->
    <move todir="${temp.lib.dir}/${lib.name}">
        <fileset dir="${temp.lib.dir}/${lib.name}" includes="**/*.properties"/>
        <mapper type="glob" from="*.properties" to="${lib.name}-*.properties"/>
    </move>
    <!-- 把处理后的文件移到总临时目录,删除子目录 -->
    <move todir="${temp.lib.dir}">
        <fileset dir="${temp.lib.dir}/${lib.name}"/>
    </move>
    <delete dir="${temp.lib.dir}/${lib.name}"/>
</target>

应用代码调整

之后你需要修改应用里加载properties的代码,比如原来的getResourceAsStream("config.properties"),要改成加载xxx-config.properties(xxx是对应依赖Jar的名称),或者根据模块需求加载对应的前缀文件。

方案二:保留嵌套Jar结构(不合并依赖Jar)

要是不想改现有代码,另一种思路是不把依赖Jar合并成普通Jar,而是把所有依赖Jar直接打包到sdk.jar里当嵌套Jar,然后写个自定义类加载器来加载这些嵌套Jar里的类和资源。

具体Ant脚本修改

把打包sdk.jar的部分改成直接打包所有依赖Jar,不解压合并:

<jar destfile="${storelibs.jar}" filesetmanifest="skip">
    <fileset dir="lib" includes="*.jar" excludes="*fonts*.jar"/>
    <!-- 加个自定义Manifest属性,方便类加载器识别嵌套Jar -->
    <manifest>
        <attribute name="Nested-Libs" value="*.jar"/>
    </manifest>
</jar>

自定义类加载器实现

然后写一个自定义类加载器,让它能从sdk.jar里读取嵌套的Jar文件并加载资源:

public class NestedJarClassLoader extends URLClassLoader {
    public NestedJarClassLoader(URL[] urls, ClassLoader parent) throws IOException {
        super(urls, parent);
        // 加载sdk.jar中的嵌套Jar
        URL sdkJarUrl = getClass().getResource("/sdk.jar");
        if (sdkJarUrl != null) {
            try (JarFile jarFile = new JarFile(new File(sdkJarUrl.toURI()))) {
                Enumeration<JarEntry> entries = jarFile.entries();
                while (entries.hasMoreElements()) {
                    JarEntry entry = entries.nextElement();
                    if (!entry.isDirectory() && entry.getName().endsWith(".jar")) {
                        // 把嵌套Jar转换成URL加入类加载器
                        URL nestedJarUrl = new URL("jar:" + sdkJarUrl + "!/" + entry.getName() + "!/");
                        addURL(nestedJarUrl);
                    }
                }
            }
        }
    }

    // 重写getResource方法,确保能找到嵌套Jar里的资源
    @Override
    public URL getResource(String name) {
        URL resource = super.getResource(name);
        if (resource == null) {
            // 这里可以扩展嵌套Jar的资源查找逻辑
        }
        return resource;
    }
}

最后修改主类的main方法,用这个自定义类加载器加载应用类,或者启动时指定这个类加载器就行。

总结

  • 方案一适合愿意调整资源加载逻辑的场景,实现简单,不用额外维护类加载器代码。
  • 方案二适合不想动现有代码的场景,但需要编写和维护自定义类加载器,复杂度稍高。

你可以根据项目实际情况选最适合的方案。

内容的提问来源于stack exchange,提问作者Amongalen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:31:23