Resumen rápido, puede hacer lo siguiente:
Incluya los módulos JavaFX a través de --module-path
y me --add-modules
gusta en la respuesta de José.
O
Una vez que haya agregado las bibliotecas JavaFX a su proyecto (ya sea manualmente o mediante la importación de maven / gradle), agregue el module-info.java
archivo similar al especificado en esta respuesta. (Tenga en cuenta que esta solución hace que su aplicación sea modular, por lo que si usa otras bibliotecas, también deberá agregar declaraciones para requerir sus módulos dentro del module-info.java
archivo).
Esta respuesta es un complemento de la respuesta de José.
La situación es esta:
- Está utilizando una versión reciente de Java, por ejemplo 13.
- Tienes una aplicación JavaFX como proyecto Maven.
- En su proyecto Maven, tiene el complemento JavaFX configurado y las dependencias JavaFX configuradas según la respuesta de José.
- Vaya al código fuente de su clase principal que extiende Aplicación, haga clic derecho sobre él e intente ejecutarlo.
- Se obtiene una
IllegalAccessError
participación de un "módulo sin nombre" al intentar iniciar la aplicación.
Extracto de un seguimiento de pila que genera una IllegalAccessError
cuando se intenta ejecutar una aplicación JavaFX desde 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
Bien, ahora estás un poco atascado y no tienes idea de lo que está pasando.
Lo que realmente sucedió es esto:
- Maven ha descargado con éxito las dependencias de JavaFX para su aplicación, por lo que no necesita descargar las dependencias por separado ni instalar un SDK de JavaFX o una distribución de módulos ni nada por el estilo.
- Idea ha importado con éxito los módulos como dependencias a su proyecto, por lo que todo se compila bien y todo el código se completa y todo funciona bien.
Entonces parece que todo debería estar bien. PERO, cuando ejecuta su aplicación, el código en los módulos JavaFX falla cuando intenta usar la reflexión para crear instancias de su clase de aplicación (cuando invoca el lanzamiento) y sus clases de controlador FXML (cuando carga FXML). Sin alguna ayuda, este uso de la reflexión puede fallar en algunos casos, generando lo oscuro IllegalAccessError
. Esto se debe a una característica de seguridad del sistema de módulos de Java que no permite que el código de otros módulos use la reflexión en sus clases a menos que usted lo permita explícitamente (y el lanzador de aplicaciones JavaFX y FXMLLoader requieren una reflexión en su implementación actual para que funcionen correctamente).
Aquí es donde algunas de las otras respuestas a esta pregunta, cuya referencia module-info.java
, entran en escena.
Entonces, tomemos un curso intensivo en módulos de Java:
La parte clave es esta:
4.9. Abre
Si necesitamos permitir la reflexión de tipos privados, pero no queremos que todo nuestro código esté expuesto, podemos usar la directiva opens para exponer paquetes específicos.
Pero recuerde, esto abrirá el paquete a todo el mundo, así que asegúrese de que eso sea lo que quiere:
module my.module { opens com.my.package; }
Entonces, tal vez no desee abrir su paquete a todo el mundo, entonces puede hacer:
4.10. Abre ... a
De acuerdo, la reflexión es genial a veces, pero aún queremos tanta seguridad como podamos obtener de la encapsulación. Podemos abrir selectivamente nuestros paquetes a una lista de módulos aprobados previamente, en este caso, usando la directiva opens ... to:
module my.module {abre com.my.package a moduleOne, moduleTwo, etc .; }
Entonces, terminas creando una clase src / main / java / module-info.java que se ve así:
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;
}
Donde, org.jewelsea.demo.javafx.springboot
es el nombre del paquete que contiene la clase de aplicación JavaFX y las clases de controlador JavaFX (reemplácelo con el nombre de paquete apropiado para su aplicación). Esto le dice al tiempo de ejecución de Java que está bien para las clases en javafx.graphics
y javafx.fxml
para invocar la reflexión sobre las clases en su org.jewelsea.demo.javafx.springboot
paquete. Una vez hecho esto, y la aplicación se compila y se vuelve a ejecutar, las cosas funcionarán bien y el IllegalAccessError
uso de reflexión generado por JavaFX ya no ocurrirá.
Pero, ¿qué pasa si no desea crear un archivo module-info.java?
Si en lugar de usar el botón Ejecutar en la barra de herramientas superior de IDE para ejecutar su clase de aplicación directamente, usted en su lugar:
- Fui a la ventana de Maven en el costado del IDE.
- Elija el destino del complemento javafx maven
javafx.run
.
- Hizo clic derecho en eso y eligió
Run Maven Build
o Debug...
.
Entonces la aplicación se ejecutará sin el module-info.java
archivo. Supongo que esto se debe a que el complemento maven es lo suficientemente inteligente como para incluir dinámicamente algún tipo de configuración que permite que las clases JavaFX reflejen la aplicación incluso sin un module-info.java
archivo, aunque no sé cómo se logra esto.
Para transferir esa configuración al botón Ejecutar en la barra de herramientas superior, haga clic con el botón derecho en el javafx.run
objetivo de Maven y elija la opción Create Run/Debug Configuration
para el objetivo. Luego, puede elegir Ejecutar en la barra de herramientas superior para ejecutar el objetivo de Maven.