我一直在研究一个命令行可执行的Java程序。它处于测试阶段,打包它已被证明是一个问题。这个问题的症结围绕着我的应用程序具有的许多依赖项。我的 lib 文件夹(包含 jars)接近 500mb。
目前我一直在使用 Eclipse Runnable Jar Export 向导。完美无缺,除了它必须从Eclipse完成。它会生成一个大小约为 500mb 的 Jar(不要问...可以说,这个程序中需要打包很多 COBOL 程序)。这个过程需要<30秒。
理想情况下,我希望这是某种 Ant 任务,可以通过 Jenkins 运行并发布到存储库。这样,用户只需抓住一个 Jar 并运行它。
我调查过的选项:
自定义类加载器(FatJar,OneJar等)。
- 工程
- 运行时执行速度很慢,因为需要解压缩 jar。
- 文件结构基本上是一个充满 jar 的 Jar,主类通过自定义类加载器委托。
-
快速构建时间(5 分钟)
<target name="hello" depends="compile"> <property name="classes.dir" value="onejar" /> <property name="build.dir" value="bin" /> <one-jar destfile="hello.jar"> <manifest> <attribute name="One-Jar-Main-Class" value="mainclass" /> <attribute name="Class-Path" value="." /> <attribute name="One-Jar-Show-Expand" value="true" /> </manifest> <main> <fileset dir="${build.dir}"/> </main> <lib> <fileset dir="lib"> <include name="**/*.jar"/> </fileset> </lib> </one-jar> </target>
手动收集依赖项,将它们解压缩到一个文件夹中,然后使用自定义 MANIFEST 文件将它们全部震撼。
- 无法让它工作
- 大 (1500MB+)
-
构建时间慢 (20分钟 +)
<target name="compile" depends="resolve"> <javac srcdir="src" destdir="bin" debug="true" deprecation="on"> <classpath> <path refid="ivy.path" /> </classpath> </javac> </target> <target name="jar" depends="compile" description="Create one big jarfile."> <jar jarfile="${dist}/deps.jar"> <zipgroupfileset dir="lib"> <include name="**/*.jar" /> </zipgroupfileset> </jar> <sleep seconds="1" /> <jar jarfile="${dist}/myjar.jar" basedir="bin"> <zipfileset src="${dist}/deps.jar" excludes="META-INF/*.SF" /> <manifest> <attribute name="Main-Class" value="mymainclassishere" /> </manifest> </jar> </target>
所以是的,有人有什么建议吗?我很想听听人们的想法。
编辑:具体来说....为什么 Eclipse Runnable Jar 导出向导可以在不到 30 秒的时间内导出我的 Jar,而我的构建时间> 30 分钟。
回答你的问题可能会不受欢迎,但我昨天确实设法解决了这个问题。在我尝试的所有选项中,我发现最快的解决方案是复制 Eclipse Runnable Jar 导出向导。它需要可以使用 Runnable Jar 导出向导获取的jar-in-jar-loader.zip
...或者你可以通过Google/Eclipse安装找到它。
我发现这个解决方案构建速度快(30 秒),运行速度快得多(5 秒启动成本)。它还使罐子结构非常整洁,没有爆炸的罐子。
<target name="compile" depends="resolve">
<mkdir dir="bin"/>
<!-- Copy all non java resources since jar javac will exclude them by default. Needed for xmls, properties etc --->
<copy todir="bin">
<fileset dir="src" excludes="**/*.java" />
</copy>
<javac srcdir="src" destdir="bin" debug="true" deprecation="on">
<classpath>
<path refid="ivy.path" />
</classpath>
</javac>
</target>
<!-- Creates the runnable jar. Copies the dependencies as jar files, into the top level of a new jar.
This means nothing without a custom classloader and manifest with a jar listing -->
<target name="jar" depends="compile" description="Create one big jarfile.">
<path id="dependencies.path">
<fileset dir="lib">
<include name="**/*.jar" />
</fileset>
</path>
<pathconvert property="manifest.classpath" pathsep=" ">
<path refid="dependencies.path" />
<mapper>
<chainedmapper>
<flattenmapper />
<globmapper from="*.jar" to="*.jar" />
</chainedmapper>
</mapper>
</pathconvert>
<mkdir dir="${dist}"/>
<jar destfile="${dist}/jar/Runnable.jar" filesetmanifest="mergewithoutmain">
<manifest>
<attribute name="Main-Class" value="org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader" />
<attribute name="Rsrc-Main-Class" value="mymainclass" />
<attribute name="Class-Path" value="." />
<attribute name="Rsrc-Class-Path" value="./ ${manifest.classpath}" />
</manifest>
<fileset dir="bin" />
<zipfileset src="jar-in-jar-loader.zip" />
<zipfileset dir="lib" includes="**/*.jar*" />
</jar>
</target>