将目录中的所有Jar包包含在Java类路径中

1177

有没有一种方法可以在类路径中包含目录内的所有jar文件?

我正在尝试使用java -classpath lib/*.jar:. my.package.Program,但它无法找到肯定在这些jar文件中的类文件。我需要单独将每个jar文件添加到类路径中吗?


4
抱歉,我从未接受过这个。应该是一个社区维护的Wiki页面。我从未使用过提供的任何答案。我相信我创建了一个Shell脚本,只需扫描lib/目录并通过解析文件名来创建类路径。 - Chris Serra
这个新的Java特性存在某种错误,因为它不能按照描述的那样工作。我放弃了并使用Ant来解决它,正如其中一个答案所描述的那样。 - Alex R
1
Windows中通配符处理存在问题。 http://stackoverflow.com/questions/11607873/escape-wildcard-processing-in-java-classpath/11608165#comment15368871_11608165 - Mike
起初我以为 jar: 后面的 . 是错误的,但是...... 在 Unix 和 Windows 系统中,“当前目录”的标准符号是一个单点 (.)。 - KNU
8
简短回答:(1)去掉“.jar”这一部分,(2)在Windows上必须至少有两个部分,用“;”分隔(在其他地方通常是“:”)。例如: java -classpath ".;lib/*" Program - Evgeni Sergeev
非常抱歉,由于这是一个热门问题,我无法提出自己的问题,因此我正在寻求Java专家回答在此处flag create与create new之间的区别。https://github.com/frohoff/jdk8u-jdk/blob/master/src/solaris/classes/sun/nio/fs/UnixChannelFactory.java - Sagar Kharab
25个回答

1316
使用Java 6或更高版本,classpath选项支持通配符。请注意以下内容:
  • 使用直引号 (")
  • 使用*,而不是*.jar
Windows

java -cp "Test.jar;lib/*" my.package.MainClass

Unix

java -cp "Test.jar:lib/*" my.package.MainClass

这与Windows类似,但使用:代替;。如果您无法使用通配符,bash允许使用以下语法(其中lib是包含所有Java存档文件的目录):

java -cp "$(printf %s: lib/*.jar)"

(请注意,使用类路径与-jar选项不兼容。也请参阅:从命令提示符中执行带有多个classpath库的jar文件理解通配符 Classpath 文档中获悉:

类路径条目可以包含基本名称通配符字符*,它被视为等效于指定具有扩展名.jar.JAR的目录中所有文件列表。例如,类路径条目foo/* 指定命名为foo的目录中的所有JAR文件。仅由*组成的类路径条目会展开为当前目录中所有jar文件的列表。

包含*的类路径条目不会匹配类文件。

要在单个目录 foo 中匹配类和 JAR 文件,可以使用foo;foo/*foo/*;foo。所选择的顺序决定了在加载foo中的类和资源之前是先加载foo中的 JAR 文件还是相反。

子目录不会递归搜索。例如,foo/* 只在 foo 中查找 JAR 文件,而不是在 foo/barfoo/baz 等子目录中查找。

在扩展类路径中枚举一个目录中的 JAR 文件的顺序未指定,可能因平台而异,甚至在同一台计算机上的同一时刻也可能不同。一个良好构造的应用程序不应依赖于任何特定的顺序。如果需要特定顺序,则可以显式枚举该类路径中的 JAR 文件。

通配符的扩展是在程序的 main 方法调用之前执行的,而不是在类加载过程本身中执行的。每个包含通配符的输入类路径元素都被替换为通过枚举命名目录中的 JAR 文件生成的(可能为空的)序列。例如,如果目录 foo 包含 a.jarb.jarc.jar,那么类路径 foo/* 就会扩展为 foo/a.jar;foo/b.jar;foo/c.jar,并且该字符串将成为系统属性java.class.path的值。

CLASSPATH 环境变量与 -classpath(或 -cp)命令行选项没有任何区别。也就是说,通配符在所有这些情况下都会被识别。但是,在Class-Path jar-manifest头中不会识别类路径通配符。

注意:由于Java 8中已知的一个错误,Windows示例必须在具有尾随星号的条目之前使用反斜杠: https://bugs.openjdk.java.net/browse/JDK-8131329

5
该功能文档记录不足,似乎需要满足一些不太明显的先决条件才能按预期工作。 - Alex R
1
为最后一个 bash/tr 技巧点赞。这里的 Java/JamVM 不喜欢工作目录之外路径的通配符,但使用shell通配符+tr显式地引用每个JAR是可行的! - Supr
4
我有一个命令java -classpath /jars/*:/anotherJarsDir/* com.test.MyClass,没有引号,它可以正常工作。我想知道为什么Shell没有展开并报错? - yellavon
4
好的,我会尽力进行翻译并遵守您的要求。需要翻译的内容是:"Also don't use ~ in the -cp"。 - Sohail Si
1
你的Windows示例在Java 8或更早版本中无法工作,但可以使用以下类路径:Test.jar;lib\*。正斜杠是可以接受的,除了在星号之前和其他一些情况下。请参见https://bugs.openjdk.java.net/browse/JDK-8131329。 - philwalk
显示剩余12条评论

280

在 Windows 下这个可以工作:

java -cp "Test.jar;lib/*" my.package.MainClass

而且这个方法不起作用:

java -cp "Test.jar;lib/*.jar" my.package.MainClass

注意*.jar,所以通配符*应该独立使用。


在Linux上,以下操作有效:

java -cp "Test.jar:lib/*" my.package.MainClass

分隔符是冒号而不是分号。


25
完美的答案。需要注意两个重要事项:1)使用引号,2)只使用,不要使用.jar。 - Wim Deblauwe
5
一年八个月后,我所做的编辑包括了UNIX版本,这再次帮助了我。 :) 有趣的是,它无法识别我的*.jar文件,只能识别* - jamesmortensen
我发现类路径的顺序很重要(但我不知道为什么)。在我改变了类路径的顺序之前,我一直收到错误信息。 - user13107
2
@SebastianGodelet - 是的,那只是我在正则表达式通配符和这种符号之间混淆了,它们不一样。大多数情况下,能够帮助我解决问题的是知道一个平台上的 : 和另一个平台上的 ; 的区别。 :) 我每年只会用命令行编译一次 Java,刚好足够让我记不住如何操作,但又经常到足以让我感到烦恼。 - jamesmortensen
java -cp "Test.jar;lib/*" my.package.MainClass 这样做不会在 lib 文件夹下寻找子文件夹吗?例如,我的 jar 包在 lib/org/abc.jar 目录中。 - Hardik Rana
显示剩余2条评论

76
我们可以通过部署一个包含清单文件(Manifest.mf)并指定其他必需的jar包的类路径的 jar 文件myapp.jar来解决这个问题,然后将其与所需的其他文件一起部署。在这种情况下,运行代码时只需要声明java -jar myapp.jar即可。
因此,如果您将主jar部署到某个目录中,然后将依赖的Jar包放入该目录下的lib文件夹中,清单文件看起来像:
Manifest-Version: 1.0
Implementation-Title: myapp
Implementation-Version: 1.0.1
Class-Path: lib/dep1.jar lib/dep2.jar

NB:这是平台无关的——我们可以使用相同的jar文件在UNIX服务器或Windows PC上启动。


这似乎对很多人有效,然而在这里Java似乎明显忽略了清单文件中的Class-Path条目。我们无法在没有使用-cp手动添加“lib / *”到类路径的情况下运行应用程序。有什么想法吗? - Raku
6
oxbow_lakes的回答并不完全正确;如果你使用java -jar myapp.jar启动此jar文件,那么Class-Path会被使用(仅会使用Class-Path;-cp/-classpath会被忽略!)。我猜oxbow_lakes想在他写'java -classpath myapp.jar'时表达这个意思。 - rzwitserloot

54

我在Ubuntu 10.04上使用java-sun 1.6.0_24解决方案,将所有jars文件放置在“lib”目录中:

java -cp .:lib/* my.main.Class

如果此命令失效,则可以尝试以下命令(将lib目录下的所有*.jar文件添加到classpath参数中):

java -cp $(for i in lib/*.jar ; do echo -n $i: ; done). my.main.Class

4
一个有趣的注解:java -cp lib/* my.main.Class 会一直失败,因为 lib/* 路径中的 shell 通配符扩展,而 java -cp .:lib/* my.main.Class 不会失败,因为 .:lib/* 不是有效的通配符路径。注意一下这点需要时间。 - albfan
1
这个不起作用;Linux会扩展。你可以尝试:java -cp '.:lib/',这样就可以正常工作了(注意单引号!双引号不行!)。实际上,如果由于冒号而不是合法的glob,则.:lib/*可能有效,但感觉有点不稳定。我会加上引号。单引号告诉bash不要触及任何内容的任何部分。 - rzwitserloot
在这种情况下,使用单引号或双引号都无所谓。你想要防止 shell 展开(globbing)*,仅此而已。并将文本 "lib/*" 直接传递给 JVM,以便 VM 将其识别为 "特殊模式" 并自行搜索 jar 文件。 - Angel O'Sphere

48

简短回答: java -classpath lib/*:. my.package.Program

Oracle提供了关于在类路径中使用通配符的文档,Java 6的文档这里和Java 7的文档这里,在理解类路径通配符的章节下。以下是要点的摘要:

  • 通常情况下,要包含给定目录中的所有JAR文件,您可以使用通配符*不是*.jar)。

  • 通配符仅匹配JAR文件,而不是类文件;要获取目录中的所有类,只需将类路径条目结束于目录名称。

  • 可以将上述两个选项组合在一起,以包括目录中的所有JAR和类文件,并应用通常的类路径优先规则。例如:-cp /classes;/jars/*

  • 通配符将不会在子目录中搜索JAR文件。

  • 如果使用CLASSPATH系统属性或-cp-classpath命令行标志,则上述要点是正确的。但是,如果使用Class-Path JAR清单头(如使用ant构建文件时所做的那样),则不会使用通配符。

是的,我的第一个链接与得分最高的答案提供的链接相同(我无法超越),但该答案未提供太多解释。由于这些天在Stack Overflow上这种行为是不鼓励的,因此我认为我应该对其进行扩展。


我的问题出在 lib/.jar 而不是 lib/. 上面的修复方法解决了问题。我注意到 : 和 ; 之间有区别,但这可能是因为我同时进行了多个更改的测试。非常感谢! - Eyad Ebrahim
感谢强调.jar之间的区别。 - burak
我不确定为什么在类路径中添加"."这个条目可以解决我的问题,但现在它可以工作了。 "."代表的是与通配符指定的目录相同的目录。我看不出来这有什么影响,但确实有效。谢谢! - Datbates

43

Windows:

 java -cp file.jar;dir/* my.app.ClassName

Linux:

 java -cp file.jar:dir/* my.app.ClassName

提示:
- Windows 路径分隔符为 ;
- Linux 路径分隔符为 :
- 如果在 Windows 中,cp 参数不含空格,则 "引号" 是可选的


Windows示例在Java 8及更早版本中无法工作:请参见https://bugs.openjdk.java.net/browse/JDK-8131329。 - philwalk
可能不适用于Open JDK,我会测试一下并在这里讨论。 - Wender
抱歉,我使用HotSpot进行了测试,认为它可以与openjdk一起使用。 - Wender
在Windows下,Oracle Java需要在星号之前使用反斜杠而不是正斜杠,尽管我还没有重新测试最新或备用的Java版本。 - philwalk
java -cp "Test.jar;lib/*" my.package.MainClass 这样做不会在 lib 文件夹下寻找子文件夹吗?例如,我的 jar 包在 lib/org/abc.jar 目录中。 - Hardik Rana

39

对我来说,在Windows中这个有效。

java -cp "/lib/*;" sample

适用于Linux操作系统

java -cp "/lib/*:" sample

我正在使用Java 6


Windows的示例似乎适用于Java 6,可能适用于Java 7,但不适用于Java 8(请参见https://bugs.openjdk.java.net/browse/JDK-8131329) - philwalk
适用于Linux上的Java 8。 - Jonathan Drapeau
java -cp "Test.jar;lib/*" my.package.MainClass 这样做不会在 lib 文件夹下寻找子文件夹吗?例如,我的 jar 文件位于 lib/org/abc.jar 目录中。 - Hardik Rana

31

4
这个方法可行,但要注意,在-jar之前加上-Djava.ext.dirs= - Giovanni Funchal
6
java.ext.dirs在classpath中与普通jar的工作方式非常不同。它具有更高的优先级和权限,可以在某种程度上覆盖引导类(rt.jar)中的类。 - Dennis C
谢谢。在“java version“1.8.0_221”Java(TM) SE Runtime Environment (build 1.8.0_221-b27) Java HotSpot(TM) 64-Bit Server VM (build 25.221-b27, mixed mode)”中,只有使用-D版本传递类路径才有效,传统形式无效。 - Matt Campbell
不再支持:JVMJ9VM149E java.ext.dirs=/core/dev1/lib 不再受支持。请将所需的库/ jar 文件添加到类路径中。 - troyfolger

27

正确的做法:

java -classpath "lib/*:." my.package.Program

错误的:

java -classpath "lib/a*.jar:." my.package.Program
java -classpath "lib/a*:."     my.package.Program
java -classpath "lib/*.jar:."  my.package.Program
java -classpath  lib/*:.       my.package.Program

java -classpath "lib/*:." my.package.Program 对我来说是有效的。 - Mugeesh Husain

11

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