Update Dec 14, 2020
Intellij has created a new JavaFX project wizard for Idea.
I highly recommend that, if you have an issue getting a JavaFX project to work, then use the new JavaFX project wizard to:
- Create a new project using the wizard.
- Test that the new project works.
- Copy relevant code from your old project into the new project.
The new project wizard is pretty fool-proof and very easy to use to get a working JavaFX project up and running in less than a minute.
The wizard generated project has the following features:
- Simple working sample code for a small application.
- Makes use of JavaFX graphics, controls, fxml.
- Configures a maven or gradle project to use JavaFX, with appropriate maven artifact dependencies for basic development.
- Uses recent, reasonably up-to-date JavaFX modules, with consistent versions (which is something many beginners often don't do).
- Adds a module-info.java file that works for an FXML based JavaFX application (will also work if you don't use FXML).
- Demonstrates placing an FXML file in a resource directory, and looking it up as a resource at runtime (which is something many, many beginners get wrong).
- Properly separates a Controller class from the Application class (which is also something many beginners get wrong).
- Demonstrates the proper use of @FXML injection based on ids in an FXML file (which is also something many beginners get wrong).
- Does not require the openjfx JavaFX SDK download (because JavaFX module dependencies are sourced through maven).
- Does not require manually setting VM arguments to run the application (because the appropriate module statements are in the module-info.java file rather than a VM command line).
- Allows execution and debugging of the JavaFX application directly from the IDE.
- Includes the openjfx maven plugin.
- You can probably ignore this or delete it from the generated project file if you want.
A couple of thoughts on the openjfx-maven plugin:
- For most development, I don't think you need to use it.
- If you don't need it you can remove it from your project.
- The openjfx maven plugin is not required for the execution of the application
- Although you could use it for that if you really wanted to, it doesn't provide any advantage over direct execution in the IDE as far as I can tell.
- The openjfx maven plugin can be useful for building jlink based distribution if that is something you wish to do.
- The openjfx maven plugin cannot package your application using
jpackage
, at least at the moment:
Prior Answer
Some of the info in this prior answer is still useful for understanding background information on Java platform modularity and JavaFX.
Quick summary, you can do either:
- Include the JavaFX modules via
--module-path
and --add-modules
like in José's answer.
OR
- Once you have JavaFX libraries added to your project (either manually or via maven/gradle import), add the
module-info.java
file similar to the one specified in this answer. (Note that this solution makes your app modular, so if you use other libraries, you will also need to add statements to require their modules inside the module-info.java
file).
This answer is a supplement to Jose's answer.
The situation is this:
- You are using a recent Java version, e.g. 13.
- You have a JavaFX application as a Maven project.
- In your Maven project you have the JavaFX plugin configured and JavaFX dependencies setup as per Jose's answer.
- You go to the source code of your main class which extends Application, you right-click on it and try to run it.
- You get an
IllegalAccessError
involving an "unnamed module" when trying to launch the app.
Excerpt for a stack trace generating an IllegalAccessError
when trying to run a JavaFX app from 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
OK, now you are kind of stuck and have no clue what is going on.
What has actually happened is this:
- Maven has successfully downloaded the JavaFX dependencies for your application, so you don't need to separately download the dependencies or install a JavaFX SDK or module distribution or anything like that.
- Idea has successfully imported the modules as dependencies to your project, so everything compiles OK and all of the code completion and everything works fine.
So it seems everything should be OK. BUT, when you run your application, the code in the JavaFX modules is failing when trying to use reflection to instantiate instances of your application class (when you invoke launch) and your FXML controller classes (when you load FXML). Without some help, this use of reflection can fail in some cases, generating the obscure IllegalAccessError
. This is due to a Java module system security feature that does not allow code from other modules to use reflection on your classes unless you explicitly allow it (and the JavaFX application launcher and FXMLLoader both require reflection in their current implementation in order for them to function correctly).
This is where some of the other answers to this question, which reference module-info.java
, come into the picture.
So let's take a crash course in Java modules:
The key part is this:
4.9. Opens
If we need to allow reflection of private types, but we don't want all
of our code exposed, we can use the opens directive to expose specific
packages.
But remember, this will open the package up to the entire world, so
make sure that is what you want:
module my.module { opens com.my.package; }
So, perhaps you don't want to open your package to the entire world, then you can do:
4.10. Opens … To
Okay, so reflection is great sometimes, but we still want as much security as we can get from encapsulation. We can selectively open our packages to a pre-approved list of modules, in this case, using the opens…to the directive:
module my.module {
opens com.my.package to moduleOne, moduleTwo, etc.;
}
So, you end up creating a src/main/java/module-info.java class which looks like this:
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;
}
Where org.jewelsea.demo.javafx.springboot
is the name of the package which contains the JavaFX Application class and JavaFX Controller classes (replace this with the appropriate package name for your application). This tells the Java runtime that it is OK for classes in the javafx.graphics
and javafx.fxml
to invoke reflection on the classes in your org.jewelsea.demo.javafx.springboot
package. Once this is done, and the application is compiled and re-run things will work fine and the IllegalAccessError
generated by JavaFX's use of reflection will no longer occur.
But what if you don't want to create a module-info.java file
If instead of using the Run button in the top toolbar of IDE to run your application class directly, you instead:
- Went to the Maven window on the side of the IDE.
- Chose the JavaFX maven plugin target
javafx.run
.
- Right-clicked on that and chose either
Run Maven Build
or Debug...
.
Then the app will run without the module-info.java
file. I guess this is because the maven plugin is smart enough to dynamically include some kind of settings that allows the app to be reflected on by the JavaFX classes even without a module-info.java
file, though I don't know how this is accomplished.
To get that setting transferred to the Run button in the top toolbar, right-click on the javafx.run
Maven target and choose the option to Create Run/Debug Configuration
for the target. Then you can just choose Run from the top toolbar to execute the Maven target.
module-info.java
– Explorationmodule-info.java
file in your source folder and explicitly require whichever JavaFX modules that you're using:requires javafx.controls;
,requires javafx.graphics;
, etc. – Explorationjava.lang.IllegalAccessError: class com.sun.javafx.fxml.FXMLLoaderHelper (in unnamed module ...) cannot access class com.sun.javafx.util.Utils (in module javafx.graphics) because module javafx.graphics does not export com.sun.javafx.util
. The reason was that we were running the Application class directly withoutApplication.launch
. Shared here as it is might be useful. – Refluent