Меня всегда интересовало, как Java запускает приложения JavaFX, поэтому я решил отладить этот процесс.Некоторые вещи перед остальным ответом:
- Я сделал отладку с JDK-10 для автономного настольного приложения.Некоторые быстрые взгляды на исходный код JDK-11 показывают, что процесс не изменился между версиями.
- Когда я использую
Application
, я имею в виду класс javafx.application.Application
. - Когда яиспользовать "
main
метод" Я имею в виду метод public static void main(String[] args)
.Аналогично, «основной класс» относится к классу, содержащему метод main
. - Все ссылки на исходный код указывают на репозиторий OpenJDK Mercurial.
- Практически все это является подробностью реализации иможет быть изменено без предварительного уведомления.
Сводка
Если при запуске приложения JavaFX основной класс является подклассом Application
, то средство запуска Java использует это собственный, внутренний основной класс .Этот внутренний класс отвечает за инициализацию инструментария JavaFX.После инициализации инструментария может произойти одно из двух:
- Подкласс
Application
имеет метод a main
. - В этом случае некоторый внутренний код JavaFX вызывает метод
main
.Теперь разработчики обязаны завершить запуск приложения с помощью Application.launch
.
- . Подкласс
Application
не имеет a main
метода. - В этом случае тот же внутренний код JavaFX запускает само приложение.Первый случай в конечном итоге заканчивается в том же месте, что и этот случай.
По сути, любой метод main
, объявленный в подклассах Application
, не является "нормальным" main
методы.Представьте себе такое поведение следующим образом:
- Внутренний
main
метод действует как точка входа для приложения Java - так же, как все "нормальные" main
методы - The *Метод 1062 * subclass '
main
служит дополнительной точкой входа для приложения JavaFX, где инструментарий JavaFX уже инициализирован.
Подробный ответ
Во-первых, этодело не в том, что вы "переопределяете" метод main
класса Application
;класс Application
не имеет метода main
.На самом деле происходит то, что Java использует свой собственный главный класс, когда объявленный основной класс приложения является подклассом Application
.Для потомков главный класс может быть объявлен с помощью одного из следующих:
- Указание его в командной строке (файл):
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[])
.Похоже, что этот метод делает то же самое, что и LauncherHelper.checkAndLoadMain
, за исключением более специфичного для JavaFX.
Я считаю, что этот метод похож на checkAndLoadMain
, потому что метод checkAndLoadMain
проверяет класс FXHelper
, который, очевидно, является допустимым,Однако launchApplication
необходимо проверить подкласс Application
.
Соответствующий код:
Шаг 4. Запуск JavaFX
Следующий вызываемый метод - launchApplicationWithArgs(ModuleAccess,String,String,String[])
.Этот метод отвечает за запуск инструментария JavaFX.После этого он загружает подкласс Application
и, если имеется, подкласс Preloader
как фактические экземпляры Class
.Это делается в потоке приложений JavaFX , но затем возвращается в основной поток.
Затем код выбирает один из двух путей в зависимости от наличия метода main
в Application
подкласс:
- A
main
метод существует: Перейдите к шагу 5. - A
main
метод не существует: Перейдите к шагу 6 (запустите приложение напрямую)
Соответствующий код:
Шаг 5: Вызвать main
Метод Application
Подкласс (Необязательно)
Если в подклассе Application
есть метод main
, он вызывается с помощью отражения.В настоящее время разработчик обязан продолжить процедуру запуска с помощью вызова Application.launch
.Существуют две перегрузки метода launch
:
Application.launch(String...)
Application.launch(Class,String)
Единственное отличие состоит в том, что первый вариант используетвызов Class
в качестве подкласса JavaFX Application
.Оба в итоге звонят LauncherImpl.launchApplication(Class,String[])
.Этот последний метод просто загружает Class
из Preloader
, если необходимо, и затем переходит к следующему шагу.
Соответствующий код:
Шаг 6: Завершить запуск приложения JavaFX
Теперь мы используем метод LauncherImpl.launchApplication(Class,Class,String[])
.Этот метод выполняет две простые вещи:
- Создание и запуск потока JavaFX-Launcher , который вызывает другой метод
LauncherImpl.launchApplication1(Class,Class,String[])
- Паркуйте основной поток (или любой другой поток с именем
Application.launch
) в CountDownLatch
до тех пор, пока не выйдет инструментарий JavaFX.
Метод launchApplication1
запустит инструментарий JavaFX, если онеще не было начато.Затем он продолжает реализацию стандартного жизненного цикла JavaFX.Это включает в себя создание классов Application
и, если они есть, Preloader
, а затем вызов методов init()
и start(Stage)
в подходящее время в соответствующих потоках.Этот жизненный цикл является публично определенным поведением;практически все остальное, упомянутое здесь, является подробностями реализации.
Соответствующий код:
Non- Application
MainКласс
Существует еще один способ запуска приложений 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
изменение на FXHelper
на шаге 1 не происходит.Это также означает, что шаги 2-5 не выполняются автоматически.Вместо этого вызов Application.launch
в Main
запускает этот процесс на последнем шаге: 6. Вот почему метод launchApplication1
также пытается запустить инструментарий JavaFX, так как он не был бы запущен в этом сценарии.