【问题标题】:Why combine all jars together?为什么将所有罐子组合在一起?
【发布时间】:2016-03-05 10:36:41
【问题描述】:

我已经创建了基于 JAX-RS 的 RESTful Web 服务并使用了 Jersey 嵌入式 Web 服务器。当我运行 main class 时,我的 ant 脚本成功编译代码,但它给了我错误 ClassNotFoundException。因此,在进行研究之后,我想出了解决方案,这里是 java build ant file with external jar files 。我所做的是创建了一个捆绑的 jar 文件尝试执行它并且它工作得很好。我想知道背后的原因:

  • 为什么这个解决方案有效?
  • 为什么要合并所有 jar 文件?

它是否类似于我们按照 J2EE 架构创建的 war 文件,否则服务器(比如 TOMCAT)不会提取 war 并且在我的情况下是 Jersey 嵌入式 HTTP 服务器的 jar 文件?

编辑: 这是我的蚂蚁build.xml 文件

<property name="lib.dir" value="${user.dir}/lib"/>
<property name="build.dir" value="${user.dir}/build"/>
<property name="build.lib.dir" value="${build.dir}/lib"/>
<property name="build.classes.dir" value="${build.dir}/classes"/>
<property name="src.dir" value="${user.dir}/src/main/java"/>
<property name="main.class" value="com.assignment.ConsoleServer"/>

<path id="classpath">
    <fileset dir="${lib.dir}" includes="**/*.jar"/>
</path>

<target name="clean">
    <delete dir="${build.dir}"/>
</target>

<target name="init" depends="clean">
    <!-- Create the build directory structure used by compile -->
    <mkdir dir="${build.dir}"/>
    <mkdir dir="${build.classes.dir}"/>
</target>

<target name="copy_jars" depends="init" >
    <copy todir="${build.lib.dir}" >
        <fileset dir="${lib.dir}">
            <include name="**/*.jar" />
        </fileset>
    </copy>
</target>

<target name="compile" depends="copy_jars">

    <javac srcdir="${src.dir}" destdir="${build.classes.dir}" classpathref="classpath" includeantruntime="false"/>
</target>

<target name="jar" depends="compile">
    <jar destfile="${build.dir}/${ant.project.name}.jar" basedir="${build.classes.dir}">
        <manifest>
            <attribute name="Main-Class" value="${main.class}"/>
        </manifest>
        <zipgroupfileset dir="${lib.dir}" includes="*.jar"/>
    </jar>
</target>

<target name="run" depends="jar">
    <java fork="true" classname="${main.class}">
        <classpath>
            <path refid="classpath"/>
            <path location="${build.dir}/${ant.project.name}.jar"/>
        </classpath>
    </java>
</target>

这是我的文件夹结构

附:我不是java专家,如果这个问题很愚蠢,请原谅我。

【问题讨论】:

标签: java tomcat ant jar build


【解决方案1】:

为什么这个解决方案有效?

在您的特定情况下,您可能没有在之前的部署中包含所有必要的依赖项。 (从您的问题中不清楚您最初是如何进行部署的。)

现在您已将所有应用程序和相关类文件等放入一个 JAR 文件中,并且您可能正在部署/运行该文件。它之所以有效,是因为现在它拥有运行所需的一切......这是以前没有的。

为什么要合并所有的jar文件?

在您的情况下,我怀疑这不是绝对必要的。可能有一种方法可以“部署”所有依赖项,而无需将它们组合到一个 JAR 文件中。

但是,在一种情况下,“uber-jar”具有优势。那是当 JAR 旨在成为“可执行”JAR 时,您希望能够将其作为单个文件分发/安装。 (和可执行的 JAR 文件 可以 引用外部 JAR 等,但您必须这样做 它是“脆弱的”。)

是不是类似war文件...?

虽然 WAR 文件包含 JAR 文件...以及通常 Web 容器能够理解的其他类型的资源,但有点类似。

【讨论】:

  • 请检查我编辑的问题.. 我添加了 ant 文件和文件夹结构
  • 实际上,关键是我的 ant 成功地编译了我所有的 java 代码,当我尝试通过 ant 运行该主类文件时,它会抛出 ClassNotFoundExcpetion .. 尽管我在运行时也包含类路径。由于它不起作用,所以我转向上述解决方案并且它起作用了。你是说it has everything that it needs to run,但它为什么在 jar 中而不是在类文件中。
  • 这显然与 runtime 类路径有关,而不是与 compile time 类路径有关。这也可能与您在类路径上有两个不同版本的 STAX 有关。
【解决方案2】:

该解决方案有效,因为您将所有服务类和依赖库打包在一个 jar 中。该 jar 和其中的所有内容都将位于类路径中,并且对您的执行虚拟机类加载器可见。 如果您将依赖库保留在 Jersey Web 服务器之外,则需要将它们放在其类路径中,那么您将不会得到 ClassNotFoundExcpetion

您不应该将 Web 应用程序打包在单个 jar 中。你应该创建一个war文件,你的依赖项将被放置在WEB-INF/lib中。然后,您可以轻松地在任何应用程序服务器上部署该战争。改用 Maven 而不是 Ant 会有很大帮助。

编辑:在您向描述和蚂蚁添加更多详细信息后

如果你不想使用 fat-jar 也可以

  • 修改您的 antjava 任务以指定将引用的类路径 所有外部库(基本上告诉 ant 如何构建 -classpathjava -jar 命令的参数
  • 更好,修改你的javac ant 任务,制作完整的Manifest 文件,正确指定Class-Path,采取更好 查看您链接的答案的解决方案(在底部)(java build ant file with external jar files

Manifest 上的完整性参考here

【讨论】:

  • 所以你的意思是说我创建了战争文件,但使用的是码头嵌入式服务器,所以不知道如何在那里部署。我知道如何在 tomcat 中部署战争,但不知道码头服务器。我也使用了 maven 并使用了 intelliJ,所以它在 IDE 中运行良好.. 但我创建了 ant 构建文件只是为了检查引擎盖下的工作
  • 我从未尝试过 Jersey,但您似乎将此应用程序视为独立应用程序 - 因此您必须将所有内容打包到一个 Java 应用程序存档(jar 文件)中。或者,如果您不希望将所有依赖库打包到您的 jar 中,则只要您的 JVM 将它们放在其类路径中,您就不必这样做。
  • 查看您的 ant 文件,如果您将类路径正确传递给 ant 运行目标,您应该能够运行您的瘦 jar! - 您是否仔细检查过您是否正确执行此操作?您的清单文件是否存在于 jar 中?清单文件中的 Class-Path 值是什么? - [我修改了我的描述]
猜你喜欢
  • 1970-01-01
  • 2012-12-30
  • 2013-05-08
  • 1970-01-01
  • 1970-01-01
  • 2018-02-20
  • 2015-02-02
  • 2011-04-08
  • 2013-11-21
相关资源
最近更新 更多