Scenario
I am creating a GUI where multiple views reference the same model object.
What I am Accustom to
In Swing, if
I have been researching this problem and I have found that dependency injection along with a singleton (for the model) is my optimal design. I learned that having a setter method for the model in each of my controllers is a form of dependency injection as is passing the model through the constructor. Spring and Guice are frameworks to help that.
I tried using Spring as noted here: http://www.zenjava.com/2011/10/23/better-controller-injection/ but I ran into the problem when the configuration class tried to load a controller, it failed to load the member controllers. This may not have been a Spring issue, but I figured I didn't care enough to spend the time to get it working. Plus, I would have had to go through all of my controller files and edited the way they were created.
Having searched around and doing a lot of reading, I found that this article best expresses what I would like to be able to do: https://forums.oracle.com/thread/2301217?tstart=0. Since the article is referring to suggested improvements, these improvements do not yet exist in JavaFX. But when they do, I will be implementing them. Just for an example, being able to inject a model via the fxml:
<usevar name="model" type="com.mycom.myapp.ModelObject"/>
The DataFX Framework has a new API called DataFX-Flow. With this API you can simply inject objects in a view controller. Other that Afterburner.fx Scopes are supported here. You can find an example here: http://www.guigarage.com/2013/12/datafx-controller-framework-preview/
Update
In addition to afterburner.fx, also checkout Gluon Ignite:
Gluon Ignite allows developers to use popular dependency injection frameworks in their JavaFX applications, including inside their FXML controllers. Gluon Ignite creates a common abstraction over several popular dependency injection frameworks (currently Guice, Spring, and Dagger, but we plan at add more as the demand becomes obvious). With full support of JSR-330 Gluon Ignite makes using dependency injection in JavaFX applications trivial.
Injection of model objects into controllers is also via @Inject, similar to afterburner.fx.
Suggested Approach
As you appear to be seeking a dependency injection framework, I think your best option is to use the afterburner.fx framework.
afterburner.fx provides a way injecting model objects into your JavaFX controllers using the standard Java @Inject annotation.
Alternative Dependency Injection Systems
Spring is large and complicated and, unless you need a lot of its other functionality for your application, should not be considered due to its complexity.
Guice is a lot simpler than Spring and a reasonable pick if you need a dependency injection framework with numerous features such as provider classes. But from the sound of it, you don't need all the features that Guice provides as you just want to a way to pass around singleton instances of objects in your application without explicitly looking them up.
So, try out afterburner.fx and see if it fits your needs.
afterburner.fx Sample Code
Here is sample of injecting a model instance (the NotesStore
) into a controller using afterburner.fx. The sample is directly copied from the afterburner.fx documentation.
import com.airhacks.afterburner.views.FXMLView;
public class NoteListView extends FXMLView {
//usually nothing to do, FXML and CSS are automatically
//loaded and instantiated
}
public class AirpadPresenter implements Initializable {
@Inject // injected by afterburner, zero configuration required
NotesStore store;
@FXML // injected by FXML
AnchorPane noteList;
@Override
public void initialize(URL url, ResourceBundle rb) {
//view constructed from FXML
NoteListView noteListView = new NoteListView();
//fetching and integrating the view from FXML
Parent view = noteListView.getView();
this.noteList.getChildren().add(view);
}
}
followme.fx is a basic sample application demonstrating how to use afterburner.fx. I did have a few issues getting followme.fx running straight out of the box due to Maven dependency incompatibilities, so I forked it's code and fixed some of the issues which prevented me from using it out of the box.
Answers to addition questions from comments
So from the NoteStore example, are you saying all I have to do is add the afterburner framework dependency and put @Inject on my model variable?
No, you also need to create an associated class that extends FXMLView and instantiate that with a new call (similar to how NotesListView is created in the sample code above). If you are interested in continuing to investigate the afterburner.fx framework, then use the followme.fx project as basis because it provides complete source code for a very simple executable sample using the framework.
I tried google guice and got it to work . . . you'll see in the constructor a game settings object is injected manually.
I don't think you should have to use the Guice injector manually like that. I think you can set a controller factory on an FXMLLoader instance to initiate the injection. This is how the FXMLView in afterburner.fx does it. The exact detail of the injection mechanism used in Guice is going to differ from the afterburner.fx mechanism, but I think the broad concept of setting the controller factory remains similar.
There is a demo of the set controller factory using FXML and Guice in the answer to: Associating FXML and Controller in Guice's Module configuration.
It's a shame there is not a more straightforward way of doing this which does not cause you so many difficulties.
As an inconsequential personal side note, I'm kind of ambivalent on the topic of dependency injection frameworks in general. Sure, they can help, but many times for simple things I'm often OK with a singleton with a getInstance method rather than the more sophisticated framework. Still I do see how in larger projects they can be useful and certainly they are very popular in certain Java frameworks.