将外部jar文件包含在使用Ant构build的新jar文件中

我刚刚“inheritance”了一个Java项目,而不是来自Java背景,有时候我有点迷路了。 Eclipse用于在开发过程中debugging和运行应用程序。 我已经通过Eclipse成功地创build了一个.jar文件,该文件包含所有需要的外部jar,比如Log4J,xmlrpc-server等等。然后,这个大的.jar文件可以成功运行:

java -jar myjar.jar 

我的下一步是使用Ant(版本1.7.1)自动化构build,所以我不需要让Eclipse来构build和部署。 由于缺乏java知识,这已经被certificate是一个挑战。 项目的根目录如下所示:

 |-> jars (where external jars have been placed) |-> java | |-> bin (where the finished .class / .jars are placed) | |-> src (Where code lives) | |-> ++files like build.xml etc |-> sql (you guessed it; sql! ) 

我的build.xml包含以下内容:

 <?xml version="1.0" encoding="UTF-8" standalone="no"?> <project basedir="." default="build" name="Seraph"> <property environment="env"/> <property name="debuglevel" value="source,lines,vars"/> <property name="target" value="1.6"/> <property name="source" value="1.6"/> <property name="build.dir" value="bin"/> <property name="src.dir" value="src"/> <property name="lib.dir" value="../jars"/> <property name="classes.dir" value="${build.dir}/classes"/> <property name="jar.dir" value="${build.dir}/jar"/> <property name="jar.file" value="${jar.dir}/seraph.jar"/> <property name="manifest.file" value="${jar.dir}/MANIFEST.MF"/> <property name="main.class" value="no.easyconnect.seraph.core.SeraphCore"/> <path id="external.jars"> <fileset dir="${lib.dir}" includes="**/*.jar"/> </path> <path id="project.classpath"> <pathelement location="${src.dir}"/> <path refid="external.jars" /> </path> <target name="init"> <mkdir dir="${build.dir}"/> <mkdir dir="${classes.dir}"/> <mkdir dir="${jar.dir}"/> <copy includeemptydirs="false" todir="${build.dir}"> <fileset dir="${src.dir}"> <exclude name="**/*.launch"/> <exclude name="**/*.java"/> </fileset> </copy> </target> <target name="clean"> <delete dir="${build.dir}"/> </target> <target name="cleanall" depends="clean"/> <target name="build" depends="init"> <echo message="${ant.project.name}: ${ant.file}"/> <javac debug="true" debuglevel="${debuglevel}" destdir="bin" source="${source}" target="${target}" classpathref="project.classpath"> <src path="${src.dir}"/> </javac> </target> <target name="build-jar" depends="build"> <delete file="${jar.file}" /> <delete file="${manifest.file}" /> <manifest file="${manifest.file}" > <attribute name="built-by" value="${user.name}" /> <attribute name="Main-Class" value="${main.class}" /> </manifest> <jar destfile="${jar.file}" basedir="${build.dir}" manifest="${manifest.file}"> <fileset dir="${classes.dir}" includes="**/*.class" /> <fileset dir="${lib.dir}" includes="**/*.jar" /> </jar> </target> </project> 

然后我运行:ant clean build-jar

并将一个名为seraph.jar的文件放在java / bin / jar-directory中。 然后我尝试使用以下命令来运行此jar:java -jar bin / jar / seraph.jar

结果是在控制台输出:

 Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/log4j/Logger at no.easyconnect.seraph.core.SeraphCore.<clinit>(SeraphCore.java:23) Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Logger at java.net.URLClassLoader$1.run(URLClassLoader.java:200) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:188) at java.lang.ClassLoader.loadClass(ClassLoader.java:307) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301) at java.lang.ClassLoader.loadClass(ClassLoader.java:252) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320) ... 1 more Could not find the main class: no.easyconnect.seraph.core.SeraphCore. Program will exit. 

我怀疑我已经在build.xml文件中做了一些令人惊讶的事情,并花了两天的时间尝试更改configuration,但没有成功。 任何帮助得到这个工作非常感谢。

哦,如果我留下一些重要的信息,我很抱歉。 这是我第一次在SO上发表。

从你的ant构build文件中,我假定你想要的是创build一个不仅包含你的应用程序类,而且包含你的应用程序所需的其他JAR的内容的单个JAR归档。

然而,你的build-jar文件只是把你需要的JAR放在你自己的JAR中; 这不会像这里解释的那样工作(见注)。

尝试修改这个:

 <jar destfile="${jar.file}" basedir="${build.dir}" manifest="${manifest.file}"> <fileset dir="${classes.dir}" includes="**/*.class" /> <fileset dir="${lib.dir}" includes="**/*.jar" /> </jar> 

对此:

 <jar destfile="${jar.file}" basedir="${build.dir}" manifest="${manifest.file}"> <fileset dir="${classes.dir}" includes="**/*.class" /> <zipgroupfileset dir="${lib.dir}" includes="**/*.jar" /> </jar> 

JarJar或One-Jar项目更加灵活和强大的解决scheme。 如果上述内容不能满足您的要求,请查看这些内容。

在有回答的人的帮助下,我开始挖掘One-Jar。 经过一些死路一条(有些结果和我以前的结果完全一样,我设法让它工作。对于其他人参考,我列出了为我工作的build.xml。

 <?xml version="1.0" encoding="UTF-8" standalone="no"?> <project basedir="." default="build" name="<INSERT_PROJECT_NAME_HERE>"> <property environment="env"/> <property name="debuglevel" value="source,lines,vars"/> <property name="target" value="1.6"/> <property name="source" value="1.6"/> <property name="one-jar.dist.dir" value="../onejar"/> <import file="${one-jar.dist.dir}/one-jar-ant-task.xml" optional="true" /> <property name="src.dir" value="src"/> <property name="bin.dir" value="bin"/> <property name="build.dir" value="build"/> <property name="classes.dir" value="${build.dir}/classes"/> <property name="jar.target.dir" value="${build.dir}/jars"/> <property name="external.lib.dir" value="../jars"/> <property name="final.jar" value="${bin.dir}/<INSERT_NAME_OF_FINAL_JAR_HERE>"/> <property name="main.class" value="<INSERT_MAIN_CLASS_HERE>"/> <path id="project.classpath"> <fileset dir="${external.lib.dir}"> <include name="*.jar"/> </fileset> </path> <target name="init"> <mkdir dir="${bin.dir}"/> <mkdir dir="${build.dir}"/> <mkdir dir="${classes.dir}"/> <mkdir dir="${jar.target.dir}"/> <copy includeemptydirs="false" todir="${classes.dir}"> <fileset dir="${src.dir}"> <exclude name="**/*.launch"/> <exclude name="**/*.java"/> </fileset> </copy> </target> <target name="clean"> <delete dir="${build.dir}"/> <delete dir="${bin.dir}"/> </target> <target name="cleanall" depends="clean"/> <target name="build" depends="init"> <echo message="${ant.project.name}: ${ant.file}"/> <javac debug="true" debuglevel="${debuglevel}" destdir="${classes.dir}" source="${source}" target="${target}"> <src path="${src.dir}"/> <classpath refid="project.classpath"/> </javac> </target> <target name="build-jar" depends="build"> <delete file="${final.jar}" /> <one-jar destfile="${final.jar}" onejarmainclass="${main.class}"> <main> <fileset dir="${classes.dir}"/> </main> <lib> <fileset dir="${external.lib.dir}" /> </lib> </one-jar> </target> </project> 

我希望别人能从中受益。

切斯尔是对的。 类加载器无法findembedded式jar。 如果你在命令行中放置了足够的debugging命令,你应该可以看到'java'命令无法将jar添加到classpath

你想做什么有时被称为“uberjar”。 我发现一个jar作为一个工具来帮助他们,但我没有尝试过。 当然还有很多其他的方法。

有两个select,要么引用你的类path中的新jar子,要么解开封装jar子中的所有类,然后重新打开jar子! 据我所知,jar子里的包装jar子是不会被推荐的,你将永远没有发现exception的类!

正如Cheesle所说,你可以解压缩你的库Jars,然后用下面的修改重新打包它们。

  <jar destfile="${jar.file}" basedir="${build.dir}" manifest="${manifest.file}"> <fileset dir="${classes.dir}" includes="**/*.class" /> <zipgroupfileset dir="${lib.dir}" includes="**/*.jar" /> </jar> 

Jar文件实际上就是embedded了清单文件的zip文件。 您可以提取并重新打包依赖Jar到您的应用程序的Jar文件。

http://ant.apache.org/manual/Tasks/zip.html“Zip任务还支持将多个zip文件合并到zip文件中,这可以通过任何嵌套的文件集的src属性或通过使用特殊的嵌套文件集zipgroupfileset“。;

请注意涉及您的依赖库的许可证。 外部链接到一个图书馆,并在您的应用程序包括图书馆合法是非常不同的事情。

编辑1:帮我慢打字。 格罗德里格斯打败了我。 🙂

编辑2:如果你决定你不能将你的依赖包括到你的应用程序中,那么你必须在启动时通过命令行或者通过Manifest文件在你的Jar的类path中指定它们。 在ANT中有一个很好的命令来处理Manifest文件中类path的特殊格式。

 <manifestclasspath property="manifest.classpath" jarfile="${jar.file}"> <classpath location="${lib.dir}" /> </manifestclasspath> <manifest file="${manifest.file}" > <attribute name="built-by" value="${user.name}" /> <attribute name="Main-Class" value="${main.class}" /> <attribute name="Class-Path" value="${manifest.classpath}" /> </manifest> 

运行可执行jar时,这是一个类path问题,如下所示:

 java -jar myfile.jar 

解决这个问题的一种方法是在java命令行上设置类path,如下所示,添加缺less的log4j jar:

 java -cp myfile.jar:log4j.jar:otherjar.jar com.abc.xyz.MyMainClass 

当然,最好的解决scheme是将类path添加到jar清单中,以便我们可以使用“-jar”java选项:

 <jar jarfile="myfile.jar"> .. .. <manifest> <attribute name="Main-Class" value="com.abc.xyz.MyMainClass"/> <attribute name="Class-Path" value="log4j.jar otherjar.jar"/> </manifest> </jar> 

以下答案演示了如何使用manifestclasspath自动执行类path清单条目的播放

在用Ant编译的文件中找不到主类

我正在使用NetBeans,也需要一个解决scheme。 在谷歌周围,从克里斯托弗的答案开始,我设法build立一个脚本,可以帮助您轻松地在NetBeans中做到这一点。 我把这些指示放在这里,以防别人需要它们。

你必须做的是下载一个jar子。 你可以使用这里的链接: http ://one-jar.sourceforge.net/index.php?page=getting-started&file=ant提取jar文件并寻找包含一个jar包的dist文件夹, ant- task.jar,one-jar-ant-task.xml和one-jar-boot- .jar。 提取它们或将它们复制到我们将添加到下面的脚本的path中,作为属性one-jar.dist.dir的值。

只需在/ project标签之前将build.xml脚本的末尾(来自NetBeans项目)复制以下脚本,将one-jar.dist.dir的值replace为正确的path并运行one-jar目标即可。

对于那些不熟悉运行目标的人,本教程可能会帮助您: http : //www.oracle.com/technetwork/articles/javase/index-139904.html 。 它还告诉你如何将来源放入一个jar子,但它们是爆炸的,而不是压缩成jar子。

 <property name="one-jar.dist.dir" value="path\to\one-jar-ant"/> <import file="${one-jar.dist.dir}/one-jar-ant-task.xml" optional="true" /> <target name="one-jar" depends="jar"> <property name="debuglevel" value="source,lines,vars"/> <property name="target" value="1.6"/> <property name="source" value="1.6"/> <property name="src.dir" value="src"/> <property name="bin.dir" value="bin"/> <property name="build.dir" value="build"/> <property name="dist.dir" value="dist"/> <property name="external.lib.dir" value="${dist.dir}/lib"/> <property name="classes.dir" value="${build.dir}/classes"/> <property name="jar.target.dir" value="${build.dir}/jars"/> <property name="final.jar" value="${dist.dir}/${ant.project.name}.jar"/> <property name="main.class" value="${main.class}"/> <path id="project.classpath"> <fileset dir="${external.lib.dir}"> <include name="*.jar"/> </fileset> </path> <mkdir dir="${bin.dir}"/> <!-- <mkdir dir="${build.dir}"/> --> <!-- <mkdir dir="${classes.dir}"/> --> <mkdir dir="${jar.target.dir}"/> <copy includeemptydirs="false" todir="${classes.dir}"> <fileset dir="${src.dir}"> <exclude name="**/*.launch"/> <exclude name="**/*.java"/> </fileset> </copy> <!-- <echo message="${ant.project.name}: ${ant.file}"/> --> <javac debug="true" debuglevel="${debuglevel}" destdir="${classes.dir}" source="${source}" target="${target}"> <src path="${src.dir}"/> <classpath refid="project.classpath"/> </javac> <delete file="${final.jar}" /> <one-jar destfile="${final.jar}" onejarmainclass="${main.class}"> <main> <fileset dir="${classes.dir}"/> </main> <lib> <fileset dir="${external.lib.dir}" /> </lib> </one-jar> <delete dir="${jar.target.dir}"/> <delete dir="${bin.dir}"/> <delete dir="${external.lib.dir}"/> </target> 

祝你好运,不要忘记投票,如果它帮助你。

您可以使用一些已经内置到ant jar任务的function。

如果您转到ant jar任务的文档,并向下滚动到“合并归档”部分,则会有一个代码片段,用于包含来自“lib / main”目录中所有jar文件的所有* .class文件:

 <jar destfile="build/main/checksites.jar"> <fileset dir="build/main/classes"/> <restrict> <name name="**/*.class"/> <archives> <zips> <fileset dir="lib/main" includes="**/*.jar"/> </zips> </archives> </restrict> <manifest> <attribute name="Main-Class" value="com.acme.checksites.Main"/> </manifest> </jar> 

创build了一个带有主类“com.acme.checksites.Main”的可执行jar文件,并将lib / main中所有jar文件的所有类embedded到该文件中。

在命名空间冲突,重复以及类似的情况下,它不会做任何事情。 此外,它将包括所有的类文件,也是你不使用的,所以组合的jar文件将是全尺寸的。

如果你需要更高级的东西,看看像一个jar子和jar jar链接