JavaFX运行时主要方法

时间:2018-09-20 22:55:34

标签: java javafx

JavaFX的Hello World-Tutorial说:

  

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

我尝试了这一点,并且确实如此,我无需使用main方法就可以启动我的应用程序。

但是,当我声明一个main类的调用launch的{​​{1}}方法时,该程序仍然有效。 Application的文档说,JavaFX运行时正在创建Application类的实例,并调用Application方法。

但是JavaFX运行时如何启动?我的意思是某处必须有一个init方法,一切才能开始。因此,我想知道我是否一个人声明了main方法,是否有两个方法?

2 个答案:

答案 0 :(得分:3)

实际上,我一直对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代码将启动应用程序本身。第一种情况最终以与本例相同的地方结束。

基本上,main子类中声明的任何Application方法都不是“常规” main方法。考虑这种行为:

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

详细答案

首先,不是您“重写”了main类的Application方法; Application类没有main方法。实际发生的情况是,只要应用程序声明的主类是Application的子类,Java就会使用它自己的主类。为了后代,可以使用以下任一方法声明主类:

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

步骤

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

步骤1:加载主类

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

相关代码:

步骤2:调用main方法

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

相关代码:

步骤3:JavaFX预启动

第2步调用的代码在名为LauncherImpl的类中;具体来说,是launchApplication(String,String,String[])方法。除了更特定于JavaFX之外,此方法似乎与LauncherHelper.checkAndLoadMain类似。

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

相关代码:

第4步:JavaFX启动

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

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

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

相关代码:

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

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

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

唯一的区别是第一个选项使用调用Class作为JavaFX Application子类。两者最终都呼叫LauncherImpl.launchApplication(Class,String[])。后一种方法仅在需要时加载Class中的Preloader,然后继续进行下一步。

相关代码:

步骤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不会自动发生。相反,在最后一步中,在Application.launch中对Main的调用将开始此过程:6.这就是launchApplication1方法也尝试启动JavaFX工具箱的原因,因为它不是在这种情况下开始。

答案 1 :(得分:0)

一个可能的答案可能是,在main中声明了一个Application方法。并且,如果从Application扩展的另一个类具有自己的main方法,该代码将仍然有效,并且main的{​​{1}}方法将被忽略:: < / p>

Application

这有意义吗?