Ant打包含同名文件的多依赖库:如何保留所有文件数据?
解决Ant打包依赖Jar时同名资源文件被覆盖的问题
我完全懂你的困扰——多个依赖Jar里带着同名但内容不同的properties文件,直接用zipgroupfileset合并Jar时,后面的文件会把前面的覆盖掉,而Java类加载器默认只会挑类路径里第一个找到的同名资源,这就导致部分功能因为读不到正确的配置直接歇菜了。
下面给你两种可行的解决方案,你可以根据项目情况选:
方案一:重命名同名资源文件(修改打包逻辑)
这个思路很直接:在合并依赖Jar之前,给每个Jar里的同名properties文件加上原Jar的名称前缀,这样所有文件都能完整保留在最终的sdk.jar里,之后只要调整应用的资源加载逻辑,去对应加载带前缀的文件就行。
具体Ant脚本修改步骤
- 先确保你已经引入了
ant-contrib任务(如果没装,得先下载并加到Ant的类路径里),因为我们要用到<foreach>遍历每个依赖Jar。 - 替换你原有
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
相关产品推荐
相关产品推荐

