如何运行一个Jar文件中不是Manifest文件中的Main-Class类

230

我有一个包含4个类的JAR文件,每个类都有Main方法。我想根据需要运行其中的每一个。我正在尝试在Linux系统的命令行中运行它。

E.g. The name of my JAR is MyJar.jar

它的主要类目录结构如下:

com/mycomp/myproj/dir1/MainClass1.class
com/mycomp/myproj/dir2/MainClass2.class
com/mycomp/myproj/dir3/MainClass3.class
com/mycomp/myproj/dir4/MainClass4.class
我知道我可以在Manifest文件中指定一个类作为主类,但是否有办法通过命令行指定一些参数来运行我想要运行的任何类?
我尝试过这个:
jar cfe MyJar.jar com.mycomp.myproj.dir2.MainClass2 com/mycomp/myproj/dir2/MainClass2.class /home/myhome/datasource.properties /home/myhome/input.txt

我遇到了这个错误:

com/mycomp/myproj/dir2/MainClass2.class : no such file or directory

(在上面的命令中,'/home/myhome/datasource.properties' 和 '/home/myhome/input.txt' 是命令行参数)。


只需将它们分别打包成不同的JAR文件,再使用另一个JAR文件来保存依赖关系即可。 - Nick
14
为什么不使用单一的主类来调用特定方法(4种中的一种),基于命令行参数来选择调用哪个方法? - Can't Tell
6个回答

275

你可以在 jar 包的 Manifest 文件中不指定 Main-Class 来创建你的 jar 包。然后:

java -cp MyJar.jar com.mycomp.myproj.dir2.MainClass2 /home/myhome/datasource.properties /home/myhome/input.txt

3
根据文档,这种方法行不通:“为了让这个选项起作用,JAR文件的清单(manifest)必须包含一个形如Main-Class: classname的行。” - Thomas
"MyJar.jar中未能加载Main-Class清单属性" - Bhushan
15
托马斯是正确的。你需要用“-cp”替换“-jar”。请看我的更新后的代码。 - lxu4net
10
@chrisdew提供的答案可以在不修改/删除Main-Class属性的情况下工作,无需改变Jar文件。 - Ed Griebel

217

您可以从JAR文件执行任何具有public static void main方法的类,即使该jar文件定义了Main-Class也是如此。

执行Main-Class:

java -jar MyJar.jar  // will execute the Main-Class

执行另一个拥有public static void main方法的类:

java -cp MyJar.jar com.mycomp.myproj.AnotherClassWithMainMethod
注意:第一个使用-jar,第二个使用-cp

19
如果你想将当前目录下的所有jar包添加到类路径中(例如从lib目录运行Main类),你可以使用java -cp "./*" com.mycomp.myproj.AnotherClassWithMainMethod命令。注意双引号将./*括起来以防止UNIX机器自动扩展路径。同时注意,"./*.jar"是无法生效的。 - Petr Újezdský

39

这篇回答是给Spring Boot用户的:

如果你的JAR文件来自于一个 Spring Boot 项目,并且是使用命令 mvn package spring-boot:repackage 创建的,那么上面提到的 "-cp" 方法将不起作用。即使你可以通过 jar tvf yours.jar 命令看到该类存在于 JAR 文件中,你仍会遇到以下错误:

Error: Could not find or load main class your.alternative.class.path

在这种情况下,可以使用以下命令运行你的替代类:

java -cp yours.jar -Dloader.main=your.alternative.class.path org.springframework.boot.loader.PropertiesLauncher

据我理解,Spring Boot 的 org.springframework.boot.loader.PropertiesLauncher 类作为分派入口类,-Dloader.main 参数告诉它要运行什么。

参考:https://github.com/spring-projects/spring-boot/issues/20404


1
如果您的JAR文件是可执行的,即当您解压缩JAR文件时,所有类都以“/BOOT-INF/classes/”开头,那该怎么办? - slashdottir
它对我起作用了。你可以在上述命令的结尾处使用参数作为键/值对。 - Ram
@周,你真是救星啊。谢谢你。 - Anand Ganesh
1
使用Spring Boot,正是我一直在寻找的 - 非常感谢! - bouwerp

24
除了调用 java -jar myjar.jar com.mycompany.Myclass,您还可以将清单中的主类设置为 Dispatcher 类。 示例:
public class Dispatcher{

    private static final Map<String, Class<?>> ENTRY_POINTS =
        new HashMap<String, Class<?>>();
    static{
        ENTRY_POINTS.put("foo", Foo.class);
        ENTRY_POINTS.put("bar", Bar.class);
        ENTRY_POINTS.put("baz", Baz.class);
    }

    public static void main(final String[] args) throws Exception{

        if(args.length < 1){
            // throw exception, not enough args
        }
        final Class<?> entryPoint = ENTRY_POINTS.get(args[0]);
        if(entryPoint==null){
            // throw exception, entry point doesn't exist
        }
        final String[] argsCopy =
            args.length > 1
                ? Arrays.copyOfRange(args, 1, args.length)
                : new String[0];
        entryPoint.getMethod("main", String[].class).invoke(null,
            (Object) argsCopy);

    }
}

4
这是一个不错的替代方法!我的jar包是使用 "spring-boot:repackage" 创建的,所以我不能通过 "-cp" 的方式运行其他入口类。我不知道 spring-boot 插件对这个 jar 做了什么。在这种情况下,一个调度类会有帮助。 - Zhou

3

我认为尼克在评论中简要提到的另一个类似的选项是创建多个包装jar文件。虽然我没有尝试过,但我认为它们可以完全为空,除了清单文件之外,其中应指定要加载的主类以及将MyJar.jar包含到类路径中。

MyJar1.jar\META-INF\MANIFEST.MF

Manifest-Version: 1.0
Main-Class: com.mycomp.myproj.dir1.MainClass1
Class-Path: MyJar.jar

MyJar2.jar\META-INF\MANIFEST.MF

Manifest-Version: 1.0
Main-Class: com.mycomp.myproj.dir2.MainClass2
Class-Path: MyJar.jar

等等,只需要使用 java -jar MyJar2.jar 来运行它即可。


1

首先,jar 命令是用来创建 jar 包的,而不是运行它。请尝试使用 java -jar 命令。

其次,为什么要将类名作为 FQCN (com.mycomp.myproj.dir2.MainClass2) 和文件名 (com/mycomp/myproj/dir2/MainClass2.class) 两次传递呢?

编辑:

看起来 java -jar 命令需要指定一个主类。您可以尝试使用 java -cp your.jar com.mycomp.myproj.dir2.MainClass2 ... 命令。其中 -cp 参数设置了 jar 包的 classpath,并使得 java 可以在其中查找主类。


我按照这里提到的做法进行了操作:http://download.oracle.com/javase/tutorial/deployment/jar/appman.html - Bhushan
1
从那个页面上看,“它可以在创建或更新jar文件时使用。” - 因此,它用于设置主类属性,而不是运行jar。 - Thomas

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