【问题标题】:JavaFX runtime main methodJavaFX 运行时主方法
【发布时间】:2019-02-25 07:31:08
【问题描述】:

JavaFX 的Hello World-Tutorial 说:

使用 JavaFX Packager 工具创建应用程序的 JAR 文件时,JavaFX 应用程序不需要 main() 方法,该工具将 JavaFX Launcher 嵌入到 JAR 文件中。但是,包含 main() 方法很有用,这样您就可以运行在没有 JavaFX 启动器的情况下创建的 JAR 文件,例如在使用未完全集成 JavaFX 工具的 IDE 时。此外,嵌入 JavaFX 代码的 Swing 应用程序需要 main() 方法。

我试过了,确实如此,我可以在没有 main 方法的情况下启动我的应用程序。

但是,当我声明 main 方法并从 Application 类调用 launch 时,程序仍然可以工作。 Application 的文档说,JavaFX 运行时正在创建 Application 类的实例并调用 init 方法。

但是 JavaFX 运行时是如何启动的呢?我的意思是在某个地方必须有一个main 方法,一切才能开始。所以我想知道如果我自己声明一个main 方法,不是有两个吗?

【问题讨论】:

  • 扩展应用程序是 JavaFX 程序所必需的。通过这样做,您在运行时默认使用应用程序中的 main 方法。如果您创建了自己的 main 方法,它将覆盖应用程序的 main ,除非您在编写任何其他代码之前调用 super.main(args)。
  • @faris mainstatic,它不能覆盖任何东西或使用super.main

标签: java javafx


【解决方案1】:

实际上我一直对 Java 如何启动 JavaFX 应用程序很感兴趣,所以我决定调试这个过程。在其余答案之前的一些事情:

  • 我使用 JDK-10 对独立桌面应用程序进行了调试。快速浏览一下 JDK-11 源代码表明该过程在版本之间没有改变。
  • 当我使用 Application 时,我指的是 javafx.application.Application 类。
  • 当我使用“main 方法”时,我指的是public static void main(String[] args) 方法。同样,“主类”是指包含main 方法的类。
  • 源代码的所有链接都指向 OpenJDK Mercurial 存储库。
  • 实际上所有这些都是实施细节,如有更改,恕不另行通知。

总结

启动 JavaFX 应用程序时,如果主类是 Application 的子类,则 Java 启动器使用 它自己的内部主类。这个内部类负责初始化 JavaFX 工具包。初始化工具包后,可能会发生以下两种情况之一。

  1. Application 子类一个main 方法。
    • 在这种情况下,一些内部 JavaFX 代码会调用 main 方法。现在由开发人员负责通过Application.launch 完成启动应用程序。
  2. Application 子类没有 main 方法。
    • 在这种情况下,相同的内部 JavaFX 代码会启动应用程序本身。第一个案例最终会在同一个地方结束。

基本上,在Application 子类中声明的任何main 方法都不是“正常的”main 方法。想想这样的行为:

  • 内部 main 方法充当 Java 应用程序的入口点,就像所有“普通”main 方法一样
  • Application 子类的main 方法用作 JavaFX 应用程序的可选入口点,其中 JavaFX 工具包已经初始化。

详细解答

首先,您不能“覆盖”Application 类的main 方法; Application 类没有 main 方法。实际发生的是,只要应用程序声明的主类是Application 的子类,Java 就会使用它自己的主类。对于后代,可以使用以下方法之一声明主类:

  • 在命令行(文件)上指定它:java -cp <classpath> foo.Main
  • 在命令行(模块)上指定它:java -p <modulepath> -m foo/foo.Main
  • 在 JAR 清单中指定它:Main-Class: foo.Main
  • (我忘记的另一种方式?)

步骤

这些步骤假设一个 JavaFX 应用程序。如果启动“常规”Java 应用程序,大多数情况都不会发生。

第一步:加载主类

内部类LauncherHelper 通过名为checkAndLoadMain 的方法检查并加载主类。此方法负责根据主类的声明方式(如上所述)解析主类。一旦找到,该方法检查主类是否是Application 的子类。如果是,则主类更改为静态内部类:LauncherHelper$FXHelper。然后执行一些验证并将Class 返回到本机代码。

相关代码:

第 2 步:调用main 方法

在找到、加载和验证主类之后,它会从(我假设)本机代码中调用。由于我们讨论的是 JavaFX 应用程序,因此主类现在是 LauncherHelper$FXHelper。该类的main 方法做了一件简单的事情:通过反射调用内部JavaFX 代码。

相关代码:

第 3 步:JavaFX 预启动

第 2 步调用的代码在一个名为 LauncherImpl 的类中;具体来说,launchApplication(String,String,String[]) 方法。此方法似乎与 LauncherHelper.checkAndLoadMain 执行类似的操作,但更具体到 JavaFX。

我相信这个方法类似于checkAndLoadMain,因为checkAndLoadMain 方法验证了FXHelper 类,这显然是有效的。但是,launchApplication 需要验证 Application 子类。

相关代码:

第 4 步:JavaFX 启动

下一个调用的方法是launchApplicationWithArgs(ModuleAccess,String,String,String[])。此方法负责启动 JavaFX 工具包。在此之后,它加载 Application 子类,如果存在,则将 Preloader 子类加载为实际的 Class 实例。它在 JavaFX 应用程序线程 上执行此操作,然后返回到主线程。

然后根据main 子类中main 方法的存在,代码采用两条路径之一:

  1. 存在main 方法:继续执行步骤 5。
  2. main 方法不存在: 继续第 6 步(直接启动应用程序)

相关代码:

第5步:调用Application子类的main方法(可选)

如果Application 子类中有main 方法,则通过反射调用它。现在开发人员有责任通过调用Application.launch 继续启动过程。 launch 方法有两个重载:

  1. Application.launch(String...)
  2. Application.launch(Class,String)

唯一的区别是第一个选项使用调用 Class 作为 JavaFX Application 子类。两人最终都打电话给LauncherImpl.launchApplication(Class,String[])。如果需要,后一种方法只需加载PreloaderClass,然后继续下一步。

相关代码:

第 6 步:完成启动 JavaFX 应用程序

现在我们使用LauncherImpl.launchApplication(Class,Class,String[]) 方法。这个方法做了两个简单的事情:

  1. 创建并启动调用另一个方法的 JavaFX-Launcher 线程
    • LauncherImpl.launchApplication1(Class,Class,String[])
  2. 将主线程(或任何称为 Application.launch 的线程)驻留在 CountDownLatch 中,直到 JavaFX 工具包退出。

launchApplication1 方法将启动 JavaFX 工具包(如果尚未启动)。然后它继续实现标准的 JavaFX 生命周期。这涉及创建ApplicationPreloader 类(如果存在),然后在适当的时间在适当的线程上调用init()start(Stage) 方法。这个生命周期是公开定义的行为;几乎这里提到的所有其他内容都是实现细节。

相关代码:

Application主类

还有另一种启动 JavaFX 应用程序的方法,其中主类不是Application 的子类,如下所示:

// main class
public class Main {

    public static void main(String[] args) {
        Application.launch(App.class, args);
    }

}

// JavaFX Application class
public class App extends Application {

    @Override 
    public void start(Stage primaryStage) throws Exception {
        // setup and show primaryStage
    }

}

由于Main 不是Application 的子类,因此不会在步骤1 中更改为FXHelper。这也意味着步骤 2-5 不会自动发生。相反,在Main 中对Application.launch 的调用在最后一步开始此过程: 6. 这就是launchApplication1 方法也尝试启动JavaFX 工具包的原因,因为它不会在这种情况下启动。

【讨论】:

  • 哇,感谢您提供详细的问题和您的努力!
【解决方案2】:

一个可能的答案可能是,在Application 中声明了一个main 方法。如果从Application 扩展的另一个类有自己的main 方法,代码仍然有效,Applicationmain 方法将被忽略::

class Main extends OtherMain {
    public Main(String text) {
        System.out.println(text);
    }

    /* If this method is removed, the main-method is called from OtherMain */
    public static void main(String[] args) {
        new Main("Called from Main-Class");
    }
}

class OtherMain {
    public static void main(String[] args) {
        new Main("Called from Other-Main-Class");
    }
}

这有意义吗?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-18
    • 1970-01-01
    • 2011-01-28
    • 2019-08-02
    • 1970-01-01
    • 1970-01-01
    • 2019-07-06
    相关资源
    最近更新 更多