JavaFX的Hello World-Tutorial说:
当使用 JavaFX Packager 工具创建应用程序的 JAR 文件时,JavaFX 应用程序不需要 main() 方法,该工具将 JavaFX Launcher 嵌入 JAR 文件中。但是,包含 main() 方法很有用,因此您可以运行在没有 JavaFX Launcher 的情况下创建的 JAR 文件,例如在使用未完全集成 JavaFX 工具的 IDE 时。此外,嵌入 JavaFX 代码的 Swing 应用程序需要 main() 方法。
我试过了,这是真的,我可以在没有main方法的情况下启动我的应用程序。
但是,当我从类中声明一个main方法调用时,该程序仍然有效。Application的文档说,JavaFX 运行时正在创建类的实例并调用该方法。launchApplicationApplicationinit
但是 JavaFX 运行时是如何启动的呢?我的意思是必须在main某处有一种方法,才能开始一切。所以我想知道我是否自己声明了一个main方法,不是有两个吗?
实际上,我一直对 Java 如何启动 JavaFX 应用程序很感兴趣,所以我决定调试这个过程。在回答其余部分之前的一些事情:
Application我指的是javafx.application.Application类。main方法”时,我指的是public static void main(String[] args)方法。同样,“主类”是指包含该main方法的类。启动 JavaFX 应用程序时,如果主类是其子类,Application则 Java 启动器将使用它自己的内部主类。这个内部类负责初始化 JavaFX 工具包。初始化工具包后,可能会发生以下两种情况之一。
Application子类具有一个main方法。
main方法。现在开发人员有责任通过Application.launch.Application子类没有一个main方法。
基本上,main在Application子类中声明的任何方法都不是“正常”main方法。想想这种行为:
main方法充当 Java 应用程序的入口点——就像所有“普通”main方法一样Application子类main的方法作为一个可选的入口点JavaFX应用程序,其中的JavaFX工具包已经被初始化。首先,不是“覆盖”类的main方法Application;该Application班有没有main方法。实际发生的情况是,只要应用程序声明的主类是Application. 对于后代,可以使用以下方法之一声明主类:
java -cp <classpath> foo.Mainjava -p <modulepath> -m foo/foo.MainMain-Class: foo.Main这些步骤假设一个 JavaFX 应用程序。如果启动“常规”Java 应用程序,则大部分情况不会发生。
内部类LauncherHelper通过名为 的方法检查并加载主类checkAndLoadMain。此方法负责根据主类的声明方式(如上所述)解析主类。一旦找到,此方法将检查主类是否是 的子类Application。如果是,则将主类更改为静态内部类:LauncherHelper$FXHelper。然后执行一些验证并Class返回到本机代码,我假设。
相关代码:
java.base/sun.launcher.LauncherHelperjava.base/sun.launcher.LauncherHelper.checkAndLoadMainjava.base/sun.launcher.LauncherHelper$FXHelpermain方法在找到、加载和验证主类之后,它是从(我假设)本机代码调用的。由于我们谈论的是 JavaFX 应用程序,因此主类现在是LauncherHelper$FXHelper. main这个类的方法做一件简单的事情:通过反射调用内部JavaFX代码。
相关代码:
步骤 2 调用的代码位于名为LauncherImpl;的类中。具体来说,launchApplication(String,String,String[])方法。这种方法似乎做类似的事情,LauncherHelper.checkAndLoadMain除了更特定于 JavaFX。
我相信这个方法类似于checkAndLoadMain因为该checkAndLoadMain方法验证了FXHelper类,这显然是有效的。但是,launchApplication需要验证Application子类。
相关代码:
javafx.graphics/com.sun.javafx.application.LauncherImpljavafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication下一个调用的方法是launchApplicationWithArgs(ModuleAccess,String,String,String[]). 该方法负责启动 JavaFX 工具包。在此之后,它将Application子类和子类(如果存在)加载Preloader为实际Class实例。它在JavaFX 应用程序线程上执行此操作,然后返回到主线程。
然后,根据子类中是否存在main方法,代码采用以下两种路径之一Application:
main存在一种方法:继续执行步骤 5。main方法不存在:继续执行步骤6(直接启动的应用程序)相关代码:
main方法Application(可选)如果子类中有main方法,Application则通过反射调用它。现在,开发人员有责任通过调用 来继续启动过程Application.launch。该launch方法有两个重载:
Application.launch(String...)Application.launch(Class,String)唯一的区别是第一个选项使用调用Class作为 JavaFXApplication子类。两者最终都调用了LauncherImpl.launchApplication(Class,String[])。这后一种方法简单地加载Class的Preloader,如果需要的话,然后继续到下一个步骤。
相关代码:
javafx.graphics/javafx.application.Application.launch(String...)javafx.graphics/javafx.application.Application.launch(Class,String...)javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication
现在我们在LauncherImpl.launchApplication(Class,Class,String[])方法中。这个方法做了两件简单的事情:
LauncherImpl.launchApplication1(Class,Class,String[])Application.launch)CountDownLatch停在 a 中,直到 JavaFX 工具包退出。launchApplication1如果尚未启动,该方法将启动 JavaFX 工具包。然后它继续实现标准的 JavaFX 生命周期。这涉及创建Application和(如果存在)Preloader类,然后在适当的时间在适当的线程上调用init()和start(Stage)方法。这个生命周期是公开定义的行为;这里提到的几乎所有其他内容都是实现细节。
相关代码:
javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication
javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1Application主类还有另一种启动 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
}
}
Run Code Online (Sandbox Code Playgroud)
由于Main不是Application更改为的子类,因此FXHelper在步骤 1 中不会发生。这也意味着步骤 2-5 不会自动发生。相反,调用Application.launchinMain在最后一步启动此过程: 6. 这就是该launchApplication1方法还尝试启动 JavaFX 工具包的原因,因为它不会在此场景中启动。