Java 在使用 Ant 的新 jar 文件构建中包含外部 jar 文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3770071/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Including external jar-files in a new jar-file build with Ant
提问by Christopher
I have just 'inherited' a Java-project and not coming from a Java-background I am a little lost at times. Eclipse is used to debug and run the application during development. I have through Eclipse succeeded in creating a .jar-file that 'includes' all the required external jars like Log4J, xmlrpc-server, etc. This big .jar can then be run successfully using:
我刚刚“继承”了一个 Java 项目,而不是来自 Java 背景,我有时会有点迷茫。Eclipse 用于在开发过程中调试和运行应用程序。我已经通过 Eclipse 成功创建了一个 .jar 文件,该文件“包含”了所有必需的外部 jar,如 Log4J、xmlrpc-server 等。然后可以使用以下方法成功运行这个大 .jar:
java -jar myjar.jar
My next step is to automate builds using Ant (version 1.7.1) so I don't have to involve Eclipse to do builds and deployment. This has proven to be a challenge due to my lacking java-knowledge. The root of the project looks like this:
我的下一步是使用 Ant(版本 1.7.1)自动构建,这样我就不必让 Eclipse 来进行构建和部署。由于我缺乏 Java 知识,这已被证明是一个挑战。项目的根目录如下所示:
|-> 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! )
My build.xml contains the following:
我的 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>
I then run: ant clean build-jar
然后我运行:ant clean build-jar
and a file named seraph.jar is placed in the java/bin/jar-directory. I then try to run this jar using the following command: java -jar bin/jar/seraph.jar
一个名为 seraph.jar 的文件放在 java/bin/jar-目录中。然后我尝试使用以下命令运行这个 jar:java -jar bin/jar/seraph.jar
The result is this output at the console:
结果是控制台上的输出:
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.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.
I suspect that I have done something amazingly silly in the build.xml-file and have spent the better part of two days trying variations on the configuration, to no avail. Any help on getting this working is greatly appreciated.
我怀疑我在 build.xml 文件中做了一些非常愚蠢的事情,并且花了两天的大部分时间尝试更改配置,但无济于事。非常感谢任何帮助完成这项工作。
Oh, and I'm sorry if I left some crucial information out. This is my first time posting here at SO.
哦,如果我遗漏了一些关键信息,我很抱歉。这是我第一次在 SO 上发帖。
采纳答案by Christopher
With the helpful advice from people who have answered here I started digging into One-Jar. After some dead-ends (and some results that were exactly like my previous results I managed to get it working. For other peoples reference I'm listing the build.xml that worked for me.
有了在这里回答的人的有用建议,我开始深入研究 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>
I hope someone else can benefit from this.
我希望其他人可以从中受益。
回答by Cheesle
Two options, either reference the new jars in your classpath or unpack all classes in the enclosing jars and re-jar the whole lot! As far as I know packaging jars within jars is not recommeneded and you'll forever have the class not found exception!
两个选项,要么在您的类路径中引用新的 jar,要么在封闭的 jar 中解压所有类并重新 jar 整个!据我所知,不建议在罐子中包装罐子,并且您将永远有找不到类的例外!
回答by Julian Simpson
Cheesleis right. There's no way for the classloader to find the embedded jars. If you put enough debug commands on the command line you should be able to see the 'java' command failing to add the jars to a classpath
芝士是对的。类加载器无法找到嵌入的 jar。如果您在命令行上放置了足够多的调试命令,您应该能够看到“java”命令未能将 jars 添加到类路径
What you want to make is sometimes called an 'uberjar'. I found one-jaras a tool to help make them, but I haven't tried it. Sure there's many other approaches.
你想要制作的东西有时被称为“uberjar”。我发现one-jar作为帮助制作它们的工具,但我还没有尝试过。当然还有很多其他的方法。
回答by Grodriguez
From your ant buildfile, I assume that what you want is to create a single JAR archive that will contain not only your application classes, but also the contents of other JARs required by your application.
根据您的 ant 构建文件,我假设您想要创建一个 JAR 存档,该存档不仅包含您的应用程序类,还包含您的应用程序所需的其他 JAR 的内容。
However your build-jar
file is just putting required JARs inside your own JAR; this will not work as explained here(see note).
但是,您的build-jar
文件只是将所需的 JAR 放入您自己的 JAR 中;这不会像这里解释的那样工作(见注释)。
Try to modify this:
尝试修改这个:
<jar destfile="${jar.file}"
basedir="${build.dir}"
manifest="${manifest.file}">
<fileset dir="${classes.dir}" includes="**/*.class" />
<fileset dir="${lib.dir}" includes="**/*.jar" />
</jar>
to this:
对此:
<jar destfile="${jar.file}"
basedir="${build.dir}"
manifest="${manifest.file}">
<fileset dir="${classes.dir}" includes="**/*.class" />
<zipgroupfileset dir="${lib.dir}" includes="**/*.jar" />
</jar>
More flexible and powerful solutions are the JarJaror One-Jarprojects. Have a look into those if the above does not satisfy your requirements.
回答by Tansir1
As Cheesle said, you can unpack and your library Jars and re-jar them all with the following modification.
正如 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 files are really just zip files with a manifest file embedded. You can extract and repackage the dependency Jars into your application's Jar file.
Jar 文件实际上只是嵌入了清单文件的 zip 文件。您可以将依赖项 Jars 提取并重新打包到应用程序的 Jar 文件中。
http://ant.apache.org/manual/Tasks/zip.html"The Zip task also supports the merging of multiple zip files into the zip file. This is possible through either the src attribute of any nested filesets or by using the special nested fileset zipgroupfileset."
http://ant.apache.org/manual/Tasks/zip.html“Zip 任务还支持将多个 zip 文件合并到 zip 文件中。这可以通过任何嵌套文件集的 src 属性或使用特殊的嵌套文件集 zipgroupfileset。”
Do pay attention to the licenses involved with your dependency libaries. Linking externally to a library and including the library in your application are very different things legally.
请注意与您的依赖库相关的许可证。从外部链接到库和在您的应用程序中包含库在法律上是非常不同的事情。
EDIT 1: Darn my slow typing. Grodriguez beat me to it. :)
编辑 1:该死的我打字慢。格罗德里格斯击败了我。:)
EDIT 2: If you decide you can't include your dependencies into your application then you have to specify them in your Jar's classpath either at the command line at startup or via the Manifest file. There's a nice command in ANT to handle the special formatting of the classpath in a Manifest file for you.
编辑 2:如果您决定不能将您的依赖项包含到您的应用程序中,那么您必须在启动时的命令行或通过清单文件在 Jar 的类路径中指定它们。ANT 中有一个很好的命令可以为您处理清单文件中类路径的特殊格式。
<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>
回答by Mark O'Connor
This is a classpath issue when running an executable jar as follows:
这是运行可执行 jar 时的类路径问题,如下所示:
java -jar myfile.jar
One way to fix the problem is to set the classpath on the java command line as follows, adding the missing log4j jar:
解决问题的一种方法是在java命令行上设置类路径如下,添加缺少的log4j jar:
java -cp myfile.jar:log4j.jar:otherjar.jar com.abc.xyz.MyMainClass
Of course the best solution is to add the classpath into the jar manifest so that the we can use the "-jar" java option:
当然,最好的解决方案是将类路径添加到 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>
The following answer demonstrates how you can use the manifestclasspathto automate the seeting of the classpath manifest entry
以下答案演示了如何使用manifestclasspath自动设置类路径清单条目
回答by Alex Burdusel
I'm using NetBeans and needed a solution for this also. After googleling around and starting from Christopher's answer i managed to build a script that helps you easily do this in NetBeans. I'm putting the instructions here in case someone else will need them.
我正在使用 NetBeans,也需要一个解决方案。在谷歌搜索并从 Christopher 的回答开始后,我设法构建了一个脚本,可帮助您在 NetBeans 中轻松完成此操作。我将说明放在这里,以防其他人需要它们。
What you have to do is download one-jar. You can use the link from here: http://one-jar.sourceforge.net/index.php?page=getting-started&file=antExtract the jar archive and look for one-jar\dist folder that contains one-jar-ant-task-.jar, one-jar-ant-task.xml and one-jar-boot-.jar. Extract them or copy them to a path that we will add to the script below, as the value of the property one-jar.dist.dir.
你所要做的就是下载一个jar。您可以使用此处的链接:http: //one-jar.sourceforge.net/index.php?page=getting-started&file=ant 提取 jar 存档并查找包含 one-jar-的 one-jar\dist 文件夹ant- task- .jar、one-jar-ant-task.xml 和 one-jar-boot-.jar。提取它们或将它们复制到我们将添加到下面脚本的路径中,作为属性 one-jar.dist.dir 的值。
Just copy the following script at the end of your build.xml script (from your NetBeans project), just before /project tag, replace the value for one-jar.dist.dir with the correct path and run one-jar target.
只需在 build.xml 脚本的末尾(来自您的 NetBeans 项目)复制以下脚本,就在 /project 标记之前,将 one-jar.dist.dir 的值替换为正确的路径并运行 one-jar 目标。
For those of you that are unfamiliar with running targets, this tutorial might help: http://www.oracle.com/technetwork/articles/javase/index-139904.html. It also shows you how to place sources into one jar, but they are exploded, not compressed into jars.
对于那些不熟悉运行目标的人,本教程可能会有所帮助:http: //www.oracle.com/technetwork/articles/javase/index-139904.html。它还向您展示了如何将源放入一个罐子中,但它们是分解的,而不是压缩到罐子中的。
<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>
Best of luck and don't forget to vote up if it helped you.
祝你好运,如果对你有帮助,不要忘记投票。
回答by s3v1
You can use a bit of functionality that is already built in to the ant jar task.
您可以使用一些已内置到 ant jar 任务中的功能。
If you go to The documentation for the ant jar taskand scroll down to the "merging archives" section there's a snippet for including the all the *.class files from all the jars in you "lib/main" directory:
如果您转到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>
This Creates an executable jar file with a main class "com.acme.checksites.Main", and embeds all the classes from all the jars in lib/main.
这将创建一个带有主类“com.acme.checksites.Main”的可执行 jar 文件,并将所有 jar 中的所有类嵌入到 lib/main 中。
It won't do anything clever in case of namespace conflicts, duplicates and things like that. Also, it will include all class files, also the ones that you don't use, so the combined jar file will be full size.
在命名空间冲突、重复和类似的情况下,它不会做任何聪明的事情。此外,它将包含所有类文件,以及您不使用的类文件,因此组合的 jar 文件将是完整大小。
If you need more advanced things like that, take a look at like one-jarand jar jar links
如果你需要更高级的东西,看看像one-jar和jar jar 链接