JavaFX运行时主要方法 [英] JavaFX runtime main method
问题描述
JavaFX的 Hello World-Tutorial 说:
当使用嵌入JavaFX的JavaFX Packager工具创建应用程序的JAR文件时,JavaFX应用程序不需要main()方法JAR文件中的启动器。但是,包含main()方法非常有用,因此您可以运行在没有JavaFX Launcher的情况下创建的JAR文件,例如在使用未完全集成JavaFX工具的IDE时。此外,嵌入JavaFX代码的Swing应用程序需要main()方法。
我试过这个并且它是真的,我可以启动我的应用程序没有主
方法。
但是当我声明 main $ c $时c>方法从
应用程序
类调用启动
,该程序仍然有效。
申请的文档说,JavaFX运行时正在创建 Application
类的实例,并调用 init
方法。
但JavaFX运行时是如何启动的?我的意思是必须在某个地方有一个 main
方法,以便开始一切。所以我想知道我是否自己声明一个 main
方法,不是有两个吗?
我一直对Java如何启动JavaFX应用程序感兴趣,所以我决定调试这个过程。在答案的其余部分之前的一些事情:
- 我使用JDK-10为独立桌面应用程序进行了调试。对JDK-11源代码的一些快速浏览表明该过程在版本之间没有变化。
- 当我使用
应用程序
我是引用javafx.application.Application
类。 - 当我使用
main $ c $时c>方法我指的是
public static void main(String [] args)
方法。类似地,main class指的是包含main
方法的类。 - 源代码的所有链接都指向OpenJDK Mercurial存储库。
- 几乎所有这些都是实施细节,如有更改,恕不另行通知。
摘要
启动JavaFX应用程序时,如果主类是 Application <的子类/ code>然后Java启动器使用它自己的内部主类。此内部类负责初始化JavaFX工具包。初始化工具包后,可能会发生以下两种情况之一。
-
应用程序
子类具有主
方法。
- 在这种情况下,一些内部JavaFX代码调用
main
方法。现在开发人员有责任通过Application.launch
完成启动应用程序。
- 在这种情况下,一些内部JavaFX代码调用
-
应用程序
子类没有主
方法。
- 在这种情况下,相同的内部JavaFX代码会启动应用程序本身。第一个案例最终会在这个案例的同一个地方结束。
基本上,在 Application
子类中声明的任何 main
方法都不是正常 main
方法。想象这样的行为:
- 内部
main
方法充当条目指向Java应用程序 - 就像所有普通主
方法一样 -
应用程序
子类'main
方法用作JavaFX应用程序的可选入口点,其中JavaFX工具包已经初始化。
详细答案
首先,情况并非如此覆盖应用程序
类的code> main 方法; 应用程序
类没有 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
,我认为是本机代码。
相关代码:
-
java.base / sun.launcher.LauncherHelper
-
java.base / sun.launcher.LauncherHelper.checkAndLoadMain
-
java.base / sun.launcher.LauncherHelper $ FXHelper
第2步:调用 main
方法
主要课程结束后发现,加载ed,并验证它是从(我假设)本机代码调用的。由于我们讨论的是JavaFX应用程序,因此主类现在是 LauncherHelper $ FXHelper
。这个类的 main
方法做了一件简单的事情:通过反射调用内部JavaFX代码。
相关代码:
第3步:JavaFX预启动
第2步调用的代码位于名为 LauncherImpl
的类中;具体来说, launchApplication(String,String,String [])
方法。此方法似乎与 LauncherHelper.checkAndLoadMain
类似,但更具体的是JavaFX。
我相信这种方法是类似于 checkAndLoadMain
,因为 checkAndLoadMain
方法验证了 FXHelper
类,这显然是有效的。但是, launchApplication
需要验证应用程序
子类。
相关代码:
-
javafx.graphics /com.sun.javafx.application.LauncherImpl
-
javafx。 graphics / com.sun.javafx.application.LauncherImpl.launchApplication
第4步: JavaFX Startup
下一个调用的方法是 launchApplicationWithArgs(ModuleAccess,String,String,String [])
。此方法负责启动JavaFX工具包。在此之后,它加载 Application
子类,如果存在,则加载 Preloader
子类作为实际类
个实例。它在 JavaFX应用程序线程上执行此操作,但随后返回主线程。
然后代码根据存在情况采用两种路径之一应用程序
子类中的 main
方法:
- 存在
主
方法:继续执行第5步。 -
主
方法不存在:继续执行步骤6(直接启动应用程序)
相关代码:
第5步:调用 main
方法
子类(可选)
如果有 main
方法在应用程序
子类中通过反射调用它。现在,开发人员有责任通过调用 Application.launch
来继续启动过程。 启动
方法有两个重载:
-
Application.launch(String ...)
-
Application.launch(Class,String)
唯一的区别是第一个选项使用调用 Class
作为JavaFX 应用程序
子类。两者最终都调用了 LauncherImpl.launchApplication(Class,String [])
。如果需要,后一种方法只需加载 Preloader
的 Class
,然后继续下一步。 / p>
相关代码:
-
javafx.graphics/javafx.application.Application.launch(String ...)
-
javafx.graphics/javafx.application.Application.launch(Class,String ...)
-
javafx.graphics/com.sun.javafx.application.LauncherImpl.l aunchApplication
- 注意:与步骤3中链接的方法不同。
第6步:完成启动JavaFX应用程序
现在我们在 LauncherImpl.launchApplication(Class,Class,String [])
方法中。这个方法做了两件简单的事情:
- 创建并启动 JavaFX-Launcher 线程调用另一个方法
-
LauncherImpl.launchApplication1(Class,Class,String [])
-
- 在
CountDownLatch <中停放主线程(或任何名为
Application.launch
的线程) / code>直到JavaFX工具包退出。
launchApplication1
如果尚未启动JavaFX工具包,则该方法将启动它。然后继续实现标准的JavaFX生命周期。这包括创建应用程序
,如果存在,则 Preloader
类,然后调用 init()
和在相应线程上的适当时间启动(阶段)
方法。这个生命周期是公开定义的行为;这里提到的几乎所有其他内容都是实现细节。
相关代码:
-
javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication
- 注意:同样,与步骤3和5中链接的方法不同。
-
javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1
非 - 申请
主类
还有另一种方法可以启动JavaFX应用程序,其中主类不是<$ c $的子类c>应用程序,如下所示:
//主类
公共类Main {
public static void main(String [] args){
Application.launch(App.class,args);
}
}
// JavaFX应用类
公共类应用扩展应用{
@Override
public void start(Stage primaryStage)throws Exception {
// setup and show primaryStage
}
}
由于 Main
不是 Application的子类
更改在步骤1中, FXHelper
不会发生。这也意味着步骤2-5不会自动发生。相反,在 Main
中对 Application.launch
的调用在最后一步启动此过程:6。这就是为什么 launchApplication1
方法也尝试启动JavaFX工具包,因为它不会在这种情况下启动。
The Hello World-Tutorial of JavaFX says:
The main() method is not required for JavaFX applications when the JAR file for the application is created with the JavaFX Packager tool, which embeds the JavaFX Launcher in the JAR file. However, it is useful to include the main() method so you can run JAR files that were created without the JavaFX Launcher, such as when using an IDE in which the JavaFX tools are not fully integrated. Also, Swing applications that embed JavaFX code require the main() method.
I tried this and its true, I can start my application without a main
method.
However when I declare a main
method an call launch
from the Application
class, the program still works.
The documentation of Application says, that the JavaFX runtime is creating an instance of the Application
class and calls the init
method.
But how does the JavaFX runtime start? I mean there has to be a main
method somewhere, for everything to start. So Iam wondering if I declare a main
method by myself, arent there two of them?
I've actually always been interested in how Java launches JavaFX applications, so I decided to debug the process. Some things before the rest of the answer:
- I did the debugging with JDK-10 for a standalone desktop application. Some quick glances at the JDK-11 source code suggests the process has not changed between versions.
- When I use
Application
I'm referring to thejavafx.application.Application
class. - When I use "
main
method" I'm referring to thepublic static void main(String[] args)
method. Similarly, "main class" refers to the class containing themain
method. - All links for source code point to the OpenJDK Mercurial repository.
- Virtually all of this is an implementation detail and is subject to change without notice.
Summary
When launching a JavaFX application, if the main class is a subclass of Application
then the Java launcher uses it's own, internal main class. This internal class is responsible for initializing the JavaFX toolkit. Once the toolkit is initialized, one of two things can happen.
- The
Application
subclass has amain
method.- In this case, some internal JavaFX code invokes the
main
method. It is now the developers responsibility to finish launching the application viaApplication.launch
.
- In this case, some internal JavaFX code invokes the
- The
Application
subclass does not have amain
method.- In this case, the same internal JavaFX code launches the application itself. The first case eventually ends up in the same place this case does.
Basically, any main
method declared in Application
subclasses are not "normal" main
methods. Think of this behavior like this:
- The internal
main
method acts as the entry point for the Java application—just like all "normal"main
methods - The
Application
subclass'main
method serves as an optional entry point for the JavaFX application, where the JavaFX toolkit is already initialized.
Detailed Answer
First, it is not the case that you "override" the main
method of the Application
class; the Application
class has no main
method. What actually happens is that Java uses it's own main class whenever the application's declared main class is a subclass of Application
. For posterity, a main class can be declared using one of the following:
- Specifying it on the command line (file):
java -cp <classpath> foo.Main
- Specifying it on the command line (module):
java -p <modulepath> -m foo/foo.Main
- Specifying it in the JAR manifest:
Main-Class: foo.Main
- (Another way I'm forgetting?)
The Steps
These steps assume a JavaFX application. Most of this doesn't happen if launching a "regular" Java application.
Step 1: Load the Main Class
An internal class, LauncherHelper
, checks and loads the main class via a method named checkAndLoadMain
. This method is responsible for resolving the main class based on how the main class was declared (described above). Once found, this method checks whether or not the main class is a subclass of Application
. If it is, then the main class is changed to a static inner class: LauncherHelper$FXHelper
. Then some validation is performed and the Class
is returned to, I assume, native code.
Relevant code:
java.base/sun.launcher.LauncherHelper
java.base/sun.launcher.LauncherHelper.checkAndLoadMain
java.base/sun.launcher.LauncherHelper$FXHelper
Step 2: Invoke the main
Method
After the main class has been found, loaded, and validated it is called from (I assume) native code. Since we are talking about a JavaFX application the main class is now LauncherHelper$FXHelper
. The main
method of this class does one simple thing: Invoke internal JavaFX code via reflection.
Relevant code:
Step 3: JavaFX Pre-Startup
The code invoked by step 2 is inside a class named LauncherImpl
; specifically, the launchApplication(String,String,String[])
method. This method appears to do similar things as LauncherHelper.checkAndLoadMain
except more specific to JavaFX.
I believe this method is similar to checkAndLoadMain
because the checkAndLoadMain
method validated the FXHelper
class, which is obviously valid. However, launchApplication
needs to validate the Application
subclass.
Relevant code:
javafx.graphics/com.sun.javafx.application.LauncherImpl
javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication
Step 4: JavaFX Startup
The next method called is launchApplicationWithArgs(ModuleAccess,String,String,String[])
. This method is responsible for starting the JavaFX toolkit. After this, it loads the Application
subclass and, if present, Preloader
subclass as actual Class
instances. It does this on the JavaFX Application Thread but then returns to the main thread.
The code then takes one of two paths depending on the presence of a main
method in the Application
subclass:
- A
main
method exists: Proceed to step 5. - A
main
method does not exist: Proceed to step 6 (launch the application directly)
Relevant code:
Step 5: Invoke main
Method of Application
Subclass (Optional)
If there is a main
method in the Application
subclass it is invoked via reflection. It is now the responsibility of the developer to continue the launching procedure via a call to Application.launch
. There are two overloads of the launch
method:
Application.launch(String...)
Application.launch(Class,String)
The only difference being the first option uses the calling Class
as the JavaFX Application
subclass. Both end up calling LauncherImpl.launchApplication(Class,String[])
. This latter method simply loads the Class
of the Preloader
, if needed, and then continues on to the next step.
Relevant code:
javafx.graphics/javafx.application.Application.launch(String...)
javafx.graphics/javafx.application.Application.launch(Class,String...)
javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication
- Note: Different method than the one linked in step 3.
Step 6: Finish Launching JavaFX Application
Now we're in the LauncherImpl.launchApplication(Class,Class,String[])
method. This method does two simple things:
- Create and start the JavaFX-Launcher thread which calls another method
LauncherImpl.launchApplication1(Class,Class,String[])
- Park the main thread (or whatever thread called
Application.launch
) in aCountDownLatch
until the JavaFX toolkit exits.
The launchApplication1
method will start the JavaFX toolkit if it hasn't already been started. Then it continues on to implement the standard JavaFX life-cycle. This involves creating the Application
and, if present, Preloader
classes then calling the init()
and start(Stage)
methods at the appropriate times on the appropriate threads. This life-cycle is the publicly defined behavior; virtually everything else mentioned here are implementation details.
Relevant code:
javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication
- Note: Again, different method from the ones linked in steps 3 and 5.
javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1
Non-Application
Main Class
There is another way to launch JavaFX applications where the main class is not a subclass of Application
, like so:
// 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
}
}
Since Main
is not a subclass of Application
the change to FXHelper
, in step 1, doesn't take place. This also means that steps 2-5 don't happen automatically. Instead, the call to Application.launch
in Main
starts this process at the final step: 6. That's why the the launchApplication1
method also tries to start the JavaFX toolkit since it wouldn't have been started in this scenario.
这篇关于JavaFX运行时主要方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!