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

Posted

技术标签:

【中文标题】如何从 Jar 运行一个不在其 Manifest 文件中的 Main-Class 的类【英文标题】:How to run a class from Jar which is not the Main-Class in its Manifest file 【发布时间】:2011-07-25 09:13:24 【问题描述】:

我有一个包含 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

我知道我可以在我的清单文件中指定一个类作为主类。但是有什么方法可以让我在命令行上指定一些参数来运行我想运行的任何类?

我试过了:

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 来保存依赖关系? 为什么没有单个主类根据命令行参数调用特定方法(4 个)? 【参考方案1】:

此答案适用于 Spring-boot 用户:

如果您的 JAR 来自 Spring-boot 项目并使用命令 mvn package spring-boot:repackage 创建,则上述“-cp”方法将不起作用。你会得到:

错误:无法找到或加载主类 your.alternative.class.path

即使您可以通过jar tvf yours.jar 看到 JAR 中的课程。

在这种情况下,通过以下命令运行您的替代类:

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

【讨论】:

如果你的 jar 是可执行的怎么办。即当你解压 jar 包时,所有的类都以“/BOOT-INF/classes/”开头 它对我有用。您可以在上述命令结束时将参数作为键/值对 @Zhou,你是救命稻草。谢谢。【参考方案2】:

您可以在 Manifest 文件中创建没有 Main-Class 的 jar。然后:

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

【讨论】:

根据文档,这不起作用:“为了使此选项起作用,JAR 文件的清单必须包含格式为 Main-Class: classname 的行。” “无法从 MyJar.jar 加载主类清单属性” 托马斯是对的。您需要将“-jar”替换为“-cp”。查看我更新的代码 @chrisdew 提供的答案无需通过修改/删除 Main-Class 属性来更改 Jar 文件即可工作。【参考方案3】:

您可以从 JAR 文件中执行 任何具有 public final static main 方法的类,即使 jar 文件定义了 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

【讨论】:

如果你也想要类路径上当前目录中的所有 jars(例如,从 lib 目录运行 Main 类),你可以使用 java -cp "./*" com.mycomp.myproj.AnotherClassWithMainMethod 注意 ./* 周围的双引号,防止 unix 机器扩展它.另请注意,"./*.jar" 将不起作用。【参考方案4】:

我认为尼克在 cmets 中简要提到的另一个类似选项是创建多个包装罐。我没有尝试过,但我认为除了清单文件之外它们可能是完全空的,清单文件应该指定要加载的主类以及将 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运行它

【讨论】:

【参考方案5】:

除了调用java -jar myjar.jar com.mycompany.Myclass,您还可以将 Manifest 中的主类设为 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);

    

【讨论】:

这是一个很好的替代方法!我的jar是使用“spring-boot:repackage”创建的,所以我只是不能通过“-cp”方式运行其他入口类。我不知道 spring-boot 插件对 jar 做了什么。在这种情况下,调度类会有所帮助。【参考方案6】:

首先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 设置在类路径中,并使 java 可以在那里查找主类。

【讨论】:

我按照这里提到的做了:download.oracle.com/javase/tutorial/deployment/jar/appman.html 来自该页面:“它可以在创建或更新 jar 文件时使用。” - 所以这个是用来设置主类属性的,不是用来运行jar的。

以上是关于如何从 Jar 运行一个不在其 Manifest 文件中的 Main-Class 的类的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Bash 从 JAR 中读取 MANIFEST.MF 文件

Gradle 重复输入错误:META-INF/MANIFEST.MF(或如何从 jar 中删除文件)

Jar包的清单文件manifest.MF

从 JAR 中读取 META-INF/MANIFEST.MF [重复]

使用 JAVA 从 jar 文件中读取 MANIFEST.MF 文件

java -jar命令