首页 文章

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

提问于
浏览
38

我刚刚'继承'了一个Java项目而不是来自Java背景我有时会有点迷失 . Eclipse用于在开发期间调试和运行应用程序 . 我通过Eclipse成功创建了一个.jar文件,其中包含了所有必需的外部jar,如Log4J,xmlrpc-server等 . 然后可以使用以下命令成功运行这个大的.jar:

java -jar myjar.jar

我的下一步是使用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! )

我的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目录中 . 然后我尝试使用以下命令运行此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文件中做了一些非常愚蠢的事情,并且花费了两天的时间来尝试配置变体,但无济于事 . 任何有关使这项工作的帮助非常感谢 .

哦,如果我遗漏了一些关键信息,我很抱歉 . 这是我第一次在SO发帖 .

8 回答

  • 2

    从您的ant构建文件中,我假设您想要的是创建一个JAR存档,该存档不仅包含您的应用程序类,还包含应用程序所需的其他JAR的内容 .

    但是你的 build-jar 文件只是将所需的JAR放在你自己的JAR中;这将无法解释here(见注释) .

    尝试修改这个:

    <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>
    

    更灵活,更强大的解决方案是JarJarOne-Jar项目 . 如果以上内容不符合您的要求,请查看这些内容 .

  • 0

    我正在使用NetBeans,并且还需要一个解决方案 . 谷歌搜索并从克里斯托弗的答案开始后,我设法构建了一个脚本,可以帮助您轻松地在NetBeans中执行此操作 . 我将这些说明放在这里以防其他人需要它们 .

    你要做的就是下载一个 jar . 您可以使用此处的链接:http://one-jar.sourceforge.net/index.php?page=getting-started&file=ant解压缩jar存档并查找包含one-jar-ant-task-.jar,one-jar-ant-task.xml和one-jar-boot的one-jar \ dist文件夹- . jar . 将它们解压缩或复制到我们将添加到下面脚本的路径中,作为属性one-jar.dist.dir的值 .

    只需在build.xml脚本的末尾(来自NetBeans项目)复制以下脚本,就在/ project标记之前,用正确的路径替换one-jar.dist.dir的值并运行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>
    

    祝你好运,不要忘了投票,如果它帮助你 .

  • 49

    Cheesle是对的 . 有'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'命令无法将jar添加到类路径

    你想要做什么有时被称为'uberjar' . 我发现one-jar是一种帮助制作它们的工具,但我还有许多其他方法 .

  • 0

    正如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文件实际上只是嵌入了清单文件的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."

    请注意您的依赖库中涉及的许可证 . 从外部链接到库并在应用程序中包含库是合法的非常不同的事情 .

    编辑1:打字慢打字 . 格罗德里格斯打败了我 . :)

    编辑2:如果您决定不能将依赖项包含到应用程序中,则必须在启动时的命令行或通过Manifest文件在Jar的类路径中指定它们 . 在ANT中有一个很好的命令来处理Manifest文件中类路径的特殊格式 .

    <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>
    
  • 0

    有了在这里回答的人的有用建议,我开始深入了解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>
    

    我希望其他人可以从中受益 .

  • 0

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

    java -jar myfile.jar
    

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

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

    当然最好的解决方案是将类路径添加到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自动化类路径清单条目的seeting

    Cannot find Main Class in File Compiled With Ant

  • 0

    两个选项,要么引用类路径中的新jar,要么解压缩所有在封闭的 jar 里的课程,重新包装整个!据我所知, jar 里的包装 jar 没有被推荐,你将永远不会发现这个类没有异常!

  • 15

    您可以使用已经内置于ant jar任务的一些功能 .

    如果您转到The documentation for the ant jar task并向下滚动到"merging archives"部分,则会有一个片段,其中包含您"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>
    

    这将创建一个带有主类"com.acme.checksites.Main"的可执行jar文件,并嵌入lib / main中所有jar的所有类 .

    在命名空间冲突,重复和类似事情的情况下,它不会做任何聪明的事情 . 此外,它将包括所有类文件,也包括您不使用的类文件,因此组合的jar文件将是完整大小 .

    如果您需要更高级的东西,请查看one-jarjar jar links

相关问题