An attempt to implement a compiler for OpenJFX's FXML language.
Now, in a world where OpenJFX application can be compiled to native executable using GraalVM native-image tool, it is important not to use Java reflections. Running tracing agent on each release and clicking over all interface is a major pain. This project provides a way to compile FXML files ahead of time, so, reflection configs for native image are not needed any more (at least for part of application loading FXML). Ahead of time compilation also speeds up UI loading.
javafx-fxml
's FXMLLoader class for easier migration.javafx-fxml
).fx:include
'ing something from dependency
jar, you must compile this dependency using mlfx too.invokedynamic
JVM instruction to implement javafx.event.EventHandler
at
compile time, so, event handler becomes a controller method reference in Java terms. Allowing no-arg methods as event
handlers would require generating static trampoline function for each handler to make it work.MLFX is designed to work with backends that provide reflective access to controller fields and methods. Currently, there is only Micronaut® framework based backend implemented. Micronaut version should be at least 3.4.0.
Use @CompileFXML
annotation to specify where to search for fxml files. Files found will be compiled ahead of time. Use
MLFXLoader
as direct FXMLLoader
replacement.
Add following requirements to module-info.java
:
module my.app {
...
requires io.micronaut.core;
requires io.micronaut.inject;
requires io.github.paullo612.mlfx.api.core;
...
}
Add dependency to API module and add compiler to annotation processor path. For example:
<project>
...
<dependencies>
...
<dependency>
<groupId>io.github.paullo612.mlfx.api</groupId>
<artifactId>micronaut</artifactId>
<version>0.6.2</version>
</dependency>
...
</dependencies>
...
<build>
...
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
...
<!-- Incremental compilation is completely broken in mlfx, so, recompile whole module on any change
in fxml files. -->
<fileExtensions>
<fileExtension>class</fileExtension>
<fileExtension>jar</fileExtension>
<fileExtension>fxml</fileExtension>
</fileExtensions>
<annotationProcessorPaths>
<path>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-inject-java</artifactId>
<version>3.4.0</version>
</path>
<path>
<groupId>io.github.paullo612.mlfx.compiler</groupId>
<artifactId>micronaut</artifactId>
<version>0.6.2</version>
</path>
...
</annotationProcessorPaths>
...
</configuration>
</plugin>
...
</plugins>
...
</build>
...
</project>
See sample Maven project, last commit adds mlfx support to it.
There are two new processing instructions that mlfx may need. Those are mlfxControllerType
and mlfxRootType
. mlfx
compiles ahead of time, so, it must know actual controller and root types when compiling. Consider following example:
<Car xmlns="http://javafx.com/javafx/19.0.0">
<Engine manufacturer="$controller.foo.bar.baz"/>
</Car>
There is controller reference, but what is actual controller type? Has it foo
property? What is foo
property type?
So, to make it compile, if your controller is of com.acme.CarController
type, you have to add processing instruction
to your fxml file:
<?mlfxControllerType com.acme.CarController?>
<Car xmlns="http://javafx.com/javafx/19.0.0">
<Engine manufacturer="$controller.foo.bar.baz"/>
</Car>
Then you can pass com.acme.CarController
instance to MLFXLoader
through setController
method when loading.
When controller is specified on root element, processing instruction is not needed:
<Car
xmlns="http://javafx.com/javafx/19.0.0"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.acme.CarController"
>
<Engine manufacturer="$controller.foo.bar.baz"/>
</Car>
This will compile fine without any special processing instructions.
Same applies to fx:root
elements. There is no attribute to express actual root type in fx:
namespace, so processing
instruction is mandatory in this case. If actual component class is com.acme.MusculeCar
, you can express it in FXML
like this:
<?mlfxRootType com.acme.MusculeCar?>
<fx:root
xmlns="http://javafx.com/javafx/19.0.0"
xmlns:fx="http://javafx.com/fxml/1"
type="Car"
>
<Engine manufacturer="$propertyOfMuscleCar.bar.baz"/>
</fx:root>
micronaut.mlfx.resourcesDirectory
specifies base directory where to search for fxml files. Points to project's
resources directory by default.