I work with JavaFX application and use FXML to implement MVC pattern. I already make proof of concept, and now start to create JavaFX user interface.
In my previous experience with spring MVC, there was usual to create service and inject them in controller class via annotations. But with JavaFX I cannot find any recommendations how to do that. Also I not sure am I have to put services to controller, or call a main class method from controller. The second solution holds service reference in main application class.
Please note my application runs service classes in concurrent threads. So all of them implements Runnable interface
I would avoid having the controllers needing to refer to the main application class as it introduces an extra dependency that is not really necessary. So have each controller keep a reference to the service object.
To provide the service to the controllers, you can basically use one of the techniques outlined in this question.
There are basically three ways to do this:
Creating the controller and setting it in the
FXMLLoader
directlyIn this version, you do not use the
fx:controller
attribute in the root element of your FXML file (doing so will cause an exception to be thrown).Given
and
Then you can load the FXML file with
Retrieving the controller from the
FXMLLoader
and setting the serviceIf you want to be able to use the
fx:controller
attribute, your controller class must have a no-argument constructor. In this case, you can set the service on the controller after theFXMLLoader
has completed loading. This looks like:Note that here you will likely have to refactor some code from the
initialize()
method, as that code may depend on the service, which will not have been set wheninitialize()
is invoked. Just move any such code to theinitService(...)
method. Loading the FXML file now looks likeUsing a controller factory
The third approach uses a controller factory. This is slightly more complex, but has some advantages. In particular, if your fxml file uses
fx:include
tags, the controller factory will be reused when the included fxml files are loaded, so those controllers can have a service object initialized as well. Managing included fxml files with the above two approaches is possible, but a little convoluted.The controller factory is essentially a function that maps a
Class<?>
to the controller that should be used (presumably one of that class, though there is requirement for that). The default controller factory just invokesnewInstance()
on theClass<?>
object (which is why you need a no-arg constructor). Here is a general controller factory implementation that calls a constructor taking aService
parameter if one exists, and calls the no-arg constructor if not.You can create this once and use it for any FXML you load (note that it references a single
Service
instance):This will work with the
fx:controller
attribute even if it refers to a class with a constructor taking aService
parameter (such as the first controller example above).If you are used to dependency-injection frameworks, you might be interested in afterburner.fx by Adam Bien. This works by setting a controller factory that examines the controller class for
@Inject
annotations and sets those values on the controller, so all you have to do is annotate the service field in the controller and follow the specific afterburner.fx naming conventions, and everything happens automagically.I also recommend this article, also by Adam Bien, which discusses some strategies for communicating with services from your controller (including handling concurrency issues).