包含JAR文件的类路径中包含了一个JAR文件

145

是否可以指定一个包含在另一个JAR文件中的JAR文件的Java classpath

15个回答

101
如果你想创建一个包含应用程序和所需库的单一JAR文件,有两种(我知道的)方法可以做到这一点。第一种是使用One-Jar,它使用特殊的类加载器允许嵌套JAR文件。第二种是UberJar,(或Shade),它会展开所包含的库并将所有类放在顶层JAR中。
我还应该提到,UberJar和Shade是适用于Maven1和Maven2的插件。如下所述,还可以使用assembly插件(实际上更强大,但要正确配置难得多)。

87
我们现在已经过了5年,看起来这个情况仍然是真实的。非常悲哀 :( - T3rm1
3
目前我知道的最佳方法是使用IntelliJ的jar构件。它会提取所有依赖jar包中的类并将它们放进你的一个jar包中。 - enl8enmentnow
3
在某些情况下这是不可能的,例如当您使用需要签名的JCE实现(如BouncyCastle)时。 - debuti
1
UberJar的链接需要登录凭据。是否有任何后继网页? - Thomas Weller
1
@StealthRabbi 这就是如何快速创建JAR构件的方法,非常适用于你偶尔需要这样做的情况:http://blog.jetbrains.com/idea/2010/08/quickly-create-jar-artifact/ - enl8enmentnow
显示剩余9条评论

53
你不想使用那些"解压JAR文件内容"的解决方案。它们确实会使事情变得更加困难(因为所有内容都在同一级别上被解压)。此外,可能会出现命名冲突(如果人们使用适当的包,这种情况不应该发生,但你不能总是控制这个)。
你想要的功能是Sun RFEs前25名之一:RFE 4648386,然而Sun却将其定为低优先级。我们只能希望Sun能够醒悟...
与此同时,我找到的最佳解决方案(我希望Sun能在JDK中复制)是使用自定义类加载器JarClassLoader

4
像log4j配置文件和许可证文本这样的东西,命名冲突实际上几乎是肯定会发生的。 - Michael Borgwardt
1
遗憾的是,JarClassLoader 是 GPLv3/商业许可证,因此它可能不会被“sun(现在的 Oracle)”复制,并且除非您具有内部政治影响力和时间来购买某些东西,否则不能在商业上使用。然而,我同意那些在当前目录上出错的 jar 包是一件坏事。 - Gus
对于这个答案中的想法点赞(尽管我不会点赞答案本身):你不能重新打包任何已签名的JAR文件,因此这种技术在许多情况下无法使用(例如Java的activation.jar)。 - Christopher Schultz

34

经过一些研究,我发现了一种不需要Maven或任何第三方扩展/程序的方法。

您可以在清单文件中使用“Class-Path”。

例如:

创建清单文件MANIFEST.MF

Manifest-Version: 1.0
Created-By: Bundle
Class-Path: ./custom_lib.jar
Main-Class: YourMainClass

将所有类编译并运行jar cfm Testing.jar MANIFEST.MF *.class custom_lib.jar

c代表创建存档 f表示您想要指定文件 v用于详细输入 m表示我们将传递自定义清单文件

请确保在jar包中包含了lib。您应该能够以正常方式运行jar。

基于: http://www.ibm.com/developerworks/library/j-5things6/

您需要了解有关类路径的所有其他信息,请在这里找到。


21
这个答案行不通。来自甲骨文文档(由上面的香蕉提供链接):"Class-Path 标头指向本地网络上的类或 JAR 文件,而不是 JAR 文件内部的 JAR 文件"。 - sebnukem
这个东西是否也可以在Android环境中使用,有没有相关的信息? - user502187
3
它适用于可执行的jar文件场景(已测试),但当你想将一个jar包含在库jar中时可能无法正常工作。 - Cychih
5
很抱歉,它不起作用了。一旦我将custom_lib.jar移开,该jar文件就无法执行了:( - Cychih
多个JAR文件如何在Class-Path中指定? - Abhishek Tiwari
显示剩余2条评论

23
使用zipgroupfileset标签(使用与fileset标签相同的属性);它将解压缩目录中的所有文件并添加到您的新档案文件中。 更多信息:http://ant.apache.org/manual/Tasks/zip.html 这是一个非常有用的方法,可以解决“jar-in-a-jar”问题 - 我知道这一点,因为我曾在尝试弄清楚该怎么做时搜索过此完全相同的 StackOverflow 问题。如果您想用Ant将一个jar或多个jar文件夹打包成一个jar文件,那么忘记所有这些类路径或第三方插件的东西,你所需要做的就是这样:
<jar destfile="your.jar" basedir="java/dir">
  ...
  <zipgroupfileset dir="dir/of/jars" />
</jar>

8
如果您正在使用ant(我正在使用来自eclipse的ant),您可以通过告诉ant添加它们来添加额外的jar文件...这不一定是最好的方法,如果您有一个由多个人维护的项目,但对于单人项目而言,这很容易实现。
例如,我的目标是构建.jar文件:
<jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
    <manifest>
        <attribute name="Author" value="ntg"/>
        ................................
        <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
    </manifest>
</jar>

我刚刚添加了一行代码,使它变成了这样:
<jar ....">
    <zipgroupfileset dir="${external-lib-dir}" includes="*.jar"/>
    <manifest>
        ................................
    </manifest>
</jar>

where

<property name="external-lib-dir" 
          value="C:\...\eclipseWorkspace\Filter\external\...\lib" />

这是存放外部jar包的目录。 就是这样...


6

如果不编写自己的类加载器,就无法实现。您可以将JAR包添加到JAR包的类路径中,但它们必须位于同一位置,而不能包含在主JAR包中。


2
如果您使用的是Eclipse IDE,只需将JAR导出并选择“将所需库打包到生成的JAR文件中”。Eclipse会自动将所需的依赖JAR添加到生成的JAR文件中,并生成一些Eclipse自定义类加载器,可以自动加载这些JAR文件。

2

你需要构建一个自定义的类加载器来实现这一点,或者使用第三方库来支持此功能。最好的方法是从运行时中提取jar文件,并将它们添加到类路径中(或已经将它们添加到类路径中)。


2
我使用 Maven 来构建 Java 项目,其中有一个插件叫做 maven assembly plugin
它可以实现你所要求的功能,但像其他一些建议所描述的那样 - 基本上是将所有依赖的 JAR 包解压并重新组合成一个单独的 JAR 包。

Maven的组装插件非常棘手...UberJar和Shade是Maven1和Maven2插件(这是我之前应该提到的事实,现在会这么做) - Steve Moyer

1

如果您使用Eclipse,有一种非常简单的方法。

将项目导出为“Runnable”Jar文件(从Eclipse中右键单击项目文件夹,选择“导出...”)。在配置导出设置时,请确保选择“将所需的库提取到生成的Jar中”。请记住,选择“提取...”,而不是“打包所需的库...”。

另外:您必须在导出设置中选择运行配置。因此,您可以始终在某个类中创建一个空的main()并将其用于运行配置。

无论如何,这并不是保证100%可行 - 因为您会注意到弹出消息,告诉您确保检查您包含的Jar文件的许可证以及有关不复制签名文件的内容。但是,我已经这样做了多年,从未遇到过问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接