Короткий підсумок, ви можете зробити:
Включіть модулі JavaFX через --module-path
і --add-modules
як у відповідь Хосе.
АБО
Після додавання бібліотек JavaFX до проекту (вручну або за допомогою імпорту maven / gradle), додайте module-info.java
файл, подібний до файлу, зазначеного у цій відповіді. (Зверніть увагу, що це рішення робить ваш додаток модульним, тому, якщо ви використовуєте інші бібліотеки, вам також потрібно буде додати оператори, щоб вимагати їх модулі всередині module-info.java
файлу).
Ця відповідь є доповненням до відповіді Хосе.
Ситуація така:
- Ви використовуєте останню версію Java, наприклад 13.
- У вас є програма JavaFX як проект Maven.
- У вашому проекті Maven у вас налаштовано плагін JavaFX та встановлено залежності JavaFX відповідно до відповіді Хосе.
- Ви переходите до вихідного коду вашого основного класу, який розширює додаток, клацаєте правою кнопкою миші і намагаєтесь запустити його.
- Ви отримуєте
IllegalAccessError
залучення "неназваного модуля" при спробі запустити програму.
Витяг для трасування стека, що генерує IllegalAccessError
при спробі запустити програму JavaFX від Intellij Idea:
Exception in Application start method
java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:464)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
Caused by: java.lang.RuntimeException: Exception in Application start method
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:900)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
at java.base/java.lang.Thread.run(Thread.java:830)
Caused by: java.lang.IllegalAccessError: class com.sun.javafx.fxml.FXMLLoaderHelper (in unnamed module @0x45069d0e) cannot access class com.sun.javafx.util.Utils (in module javafx.graphics) because module javafx.graphics does not export com.sun.javafx.util to unnamed module @0x45069d0e
at com.sun.javafx.fxml.FXMLLoaderHelper.<clinit>(FXMLLoaderHelper.java:38)
at javafx.fxml.FXMLLoader.<clinit>(FXMLLoader.java:2056)
at org.jewelsea.demo.javafx.springboot.Main.start(Main.java:13)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:846)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:455)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
Exception running application org.jewelsea.demo.javafx.springboot.Main
Добре, тепер ти застряг і не маєш уявлення, що відбувається.
Що насправді сталося ось що:
- Maven успішно завантажив залежності JavaFX для вашої програми, тому вам не потрібно окремо завантажувати залежності або встановлювати JavaFX SDK, дистрибутив модулів або щось подібне.
- Idea успішно імпортувала модулі як залежності до вашого проекту, тому все компілюється в порядку, і все завершення коду, і все працює нормально.
Тож, здається, все повинно бути в порядку. АЛЕ, коли ви запускаєте програму, код у модулях JavaFX не працює, коли намагається використовувати відображення для створення екземплярів екземплярів класу програми (коли ви викликаєте запуск) та класів контролера FXML (коли ви завантажуєте FXML). Без певної допомоги таке використання рефлексії може в деяких випадках зазнати невдачі, створюючи незрозуміле IllegalAccessError
. Це пов'язано з функцією безпеки системи модулів Java, яка не дозволяє коду з інших модулів використовувати відображення на ваших класах, якщо ви явно не дозволите це (і програма запуску програм JavaFX і FXMLLoader вимагають відображення в поточній реалізації, щоб вони могли функціонувати правильно).
Ось де module-info.java
з’являються деякі інші відповіді на це питання, на які посилаються .
Тож давайте пройдемо аварійний курс в модулях Java:
Ключова частина:
4.9. Відкривається
Якщо нам потрібно дозволити відображення приватних типів, але ми не хочемо, щоб викривався весь наш код, ми можемо використовувати директиву opens, щоб виставити конкретні пакети.
Але пам’ятайте, це відкриє пакет для усього світу, тому переконайтеся, що це те, що ви хочете:
module my.module { opens com.my.package; }
Отже, можливо, ви не хочете відкривати свій пакет для всього світу, тоді ви можете зробити:
4.10. Відкривається… До
Гаразд, тому роздуми часом бувають чудовими, але ми все одно хочемо отримати стільки безпеки, скільки ми можемо отримати від інкапсуляції. Ми можемо вибірково відкрити наші пакети для попередньо затвердженого списку модулів, в цьому випадку, використовуючи директиву opens…:
модуль my.module {відкриває com.my.package до moduleOne, moduleTwo тощо; }
Отже, у підсумку ви створюєте клас src / main / java / module-info.java, який виглядає так:
module org.jewelsea.demo.javafx.springboot {
requires javafx.fxml;
requires javafx.controls;
requires javafx.graphics;
opens org.jewelsea.demo.javafx.springboot to javafx.graphics,javafx.fxml;
}
Де, org.jewelsea.demo.javafx.springboot
це ім'я пакета, який містить клас JavaFX Application і класи JavaFX Controller (замініть це на відповідне ім'я пакета для вашої програми). Це вказує на час виконання Java, що для класів у javafx.graphics
і javafx.fxml
можна викликати роздуми про класи у вашому org.jewelsea.demo.javafx.springboot
пакунку. Як тільки це буде зроблено, і програма буде скомпільована та повторно запущена, все буде працювати нормально, і IllegalAccessError
генеровані в результаті відображення JavaFX більше не відбуватимуться.
Але що, якщо ви не хочете створювати файл module-info.java
Якщо замість того, щоб використовувати кнопку Виконати на верхній панелі інструментів IDE для безпосереднього запуску класу програми, ви замість цього:
- Підійшов до вікна Maven у боці IDE.
- Вибрав ціль плагіна javafx maven
javafx.run
.
- Клацнувши правою кнопкою миші на цьому, вибрали
Run Maven Build
або Debug...
.
Тоді програма працюватиме без module-info.java
файлу. Я думаю, це тому, що плагін maven досить розумний, щоб динамічно включати якісь налаштування, які дозволяють відображати додаток класами JavaFX навіть без module-info.java
файлу, хоча я не знаю, як це робиться.
Щоб перенести це налаштування на кнопку «Виконати» на верхній панелі інструментів, клацніть правою кнопкою миші на javafx.run
цілі Maven і виберіть опцію « Create Run/Debug Configuration
для цілі». Тоді ви можете просто вибрати Виконати на верхній панелі інструментів, щоб виконати ціль Maven.