There has been a very interesting thread in the JavaFX OTN forums about FXML and dependency injection. I went and downloaded Google Guice and wrote a small sample to try it all out. Things are not perfect quite yet (I still need full Unified Expression Language support added to FXML), but so very, very, very close!
What I ultimately hope to accomplish with FXML and CSS in JavaFX enterprise applications is that we (a) fully separate the visuals from the “controller”, (b) are left with a very simple “controller” that is highly testable, and (c) everything is highly modular.
CSS allows the designer to come in after the fact and modify the look of a UI without the developer’s help, and without a full build cycle. FXML allows a developer or designer to alter the structure of the view without a full build cycle. So (a) should be well in hand, especially if the controller itself can be injected rather than declared directly in the FXML file as it is today. (I’m OK with, even prefer, saying “I would like to have a FooController for this FXML file, if you please”, but what would really rock is if the FooController were an interface or abstract class rather than always being a concrete class, such that a DI framework like Guice could find the appropriate implementation and supply it).
For (b) to work, I want to get to the point where I don’t use the @FXML annotation at all. Right now we have to use it for binding things up, but when RT-14880 is fixed and we have some implicit reference to the controller, then I’ll be able to have very powerful bindings defined in the FXML document back to state located on the Controller, and I won’t need to handle it manually in java code anymore. This is going to be a big win.
A good dependency injection system like Guice makes (c) dead easy. I like Guice because it is all still very type safe and there is only a minimal amount of magic — in fact I found it very easy to understand. I have a very contrived example here which shows the bare-bones basics, and some simplified use case. Because RT-14880 hasn’t been implemented, I can’t use the really slick binding the way I’d like to, so instead I am using some scripting inside the FXML document to produce the text for the button. This sample revolves around a very simple POJO called Person:
Person.java
package fxmlapp; public class Person { private String firstName = "Richard"; public final String getFirstName() { return firstName; } }
SampleController.java
package fxmlapp; import javax.inject.Inject; public class SampleController { /** * This domain data model is supplied in the constructor */ private final Person person; /** * Notice this constructor is using JSR 330 Inject annotation, * which makes it "Guice Friendly". * @param person */ @Inject public SampleController(Person person) { this.person = person; } /** * Used within the FXML document. This is just some state which the * controller wants to expose for the view to bind to or use. * @return */ public final String getPersonName() { return person.getFirstName(); } /** * Some method which I will call from the FXML file. */ public void print() { System.out.println("Well done, " + getPersonName() + "!"); } }
First is the controller. SampleController is exceedingly simple. It has some model data which is supplied in the constructor, in this case, a Person object. The person is just a good old fashioned POJO in this case, but could be a more complicated object, or perhaps created by JAX-RS or it could be a JDBC ResultSet — anything you like. There is a method “getPersonName” which I added for the sake of the FXML file. Also there is a method called “print” which will print a message. You could use any method here you want. For example you might have a “submit” method which posts back to a server or a “refresh” method which fetches data.
Next up, the FXML file.
Sample.fxml
<?xml version="1.0" encoding="UTF-8"?> <?language javascript?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <StackPane fx:id="root" xmlns:fx="http://javafx.com/fxml"> <children> <Button fx:id="printBtn" onAction="controller.print()" /> <fx:script>printBtn.text = 'Click Me ' + controller.getPersonName() + '!';</fx:script> </children> </StackPane>
I create a StackPane who’s id is going to be “root”, and give it a single Button as a child. The button has also been given an id, which is important because that is the name by which I can refer to this button in the embedded JavaScript code. The button has an onAction handler which will invoke the print() method on the controller. Notice that this onAction handler is just some JavaScript.
Speaking of which, you will notice there is a processing instruction here which tells FXML that the scripting in this document will be JavaScript. Because I don’t yet have the ability to do complex binding expressions (and in this case I want to do a string concatenation for the Button text), I use an embedded script to get the person name from the controller and concatenate it with some text. And that’s it, very, very simple.
Now I have a Controller (SampleController) and an FXML file (Sample.fxml), but where is the code which associates the one with the other? This code lives in my SampleApp.
SampleApp.java
package fxmlapp; import com.google.inject.Guice; import com.google.inject.Injector; import javafx.application.Application; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; /** * A Guice based sample FXML Application */ public class SampleApp extends Application { /** * Create the Guice Injector, which is going to be used to supply * the Person model. */ private final Injector injector = Guice.createInjector(new Module()); @Override public void start(Stage stage) throws Exception { // Create a new Guice-based FXML Loader GuiceFXMLLoader loader = new GuiceFXMLLoader(injector); // Ask to load the Sample.fxml file, injecting an instance of a SampleController Parent root = (Parent) loader.load("Sample.fxml", SampleController.class); // Finish constructing the scene final Scene scene = new Scene(root, 320, 240); // Load up the CSS stylesheet scene.getStylesheets().add(getClass().getResource("fxmlapp.css").toString()); // Show the window stage.setScene(scene); stage.setTitle("Guiced"); stage.show(); } // Standard launcher public static void main(String[] args) { launch(args); } }
The first thing I do when the SampleApp is created is I create an instance of a Guice Injector. I need to feed it a “Module”, which looks like this:
Module.java
package fxmlapp; import com.google.inject.AbstractModule; /** * * @author Richard */ public class Module extends AbstractModule { @Override protected void configure() { bind(Person.class); } }
The Module, essentially, just tells Guice “hey, if somebody comes looking for a Person class, give it to them”. You can read more about it on the Guice website. But once I’ve created my Injector, it is time to use it.
In the “start” method of the SampleApp, I create a new GuiceFXMLLoader. This is another utility class of my creation which makes it easier to use Guice with FXML loading. I’ll show that class in a minute. With my newly created GuiceFXMLLoader, I’m going to call its “load” method, passing in the FXML file to load along with the type of Controller I’d like to create. The GuiceFXMLLoader then parses the FXML document. Guice then does its magic. It attempts to construct an instance of SampleController, but discovers that the @Inject annotated constructor requires a Person object. It then looks up in the Module, discovers a rule regarding Person objects, and goes off to create a Person object. Once it has the new Person, it creates a new SampleController, which then gets associated with the FXML document, and the root object is returned. I place this in a Scene and show it in the Stage.
Now in the screenshot above, you no doubt noticed things had also been styled via CSS. I didn’t have to do anything in the FXML document, or in the Controller, to style this application. I simply wrote a style sheet and loaded it in the SampleApp. Since my FXML document already had id’s and style classes in place, I didn’t have to do anything special.
fxmlapp.css
.root { -fx-background-color: linear-gradient(from 0% 0% to 0% 100%, #cbd0d7 0%, white 100%); } #printBtn { -fx-text-fill: #e4f3fc; -fx-font: 20pt "Tahoma Bold"; -fx-padding: 10; -fx-base: #2d4b8e } #printBtn:hover{ -fx-base: #395bae; }
I hope you enjoyed this post and maybe it has stimulated your interest to try out dependency injection via Guice (or Spring, though I haven’t tried it in a client context yet) with FXML and JavaFX. In the 2.1 release coming up in the first half of 2012 we’ll fix the above mentioned issues in FXML such that we’ll have more powerful binding and an implicit reference to the controller from FXML, such that you won’t need to use JavaScript in the FXML document to get this behavior. We’ll also have simplified the FXML somewhat such that the “
In my parting shot, here is the GuiceFXMLLoader. It is just a hacked up proof of concept, but you’ll need to to try these classes out on your own :-).
GuiceFXMLLoader
package fxmlapp; import com.google.inject.Injector; import java.io.InputStream; import java.net.URL; import javafx.fxml.FXMLLoader; import javax.inject.Inject; /** * Uses Guice to inject model state. Basically you create an instance of * GuiceFXMLLoader supplying an Injector, and then call load. The load * method takes the FXML file to load, and the controller to create and * associate with the FXML file. */ public class GuiceFXMLLoader { private final Injector injector; @Inject public GuiceFXMLLoader(Injector injector) { this.injector = injector; } // Load some FXML file, using the supplied Controller, and return the // instance of the initialized controller...? public Object load(String url, Class<?> controller) { Object instance = injector.getInstance(controller); FXMLLoader loader = new FXMLLoader(); loader.getNamespace().put("controller", instance); InputStream in = null; try { try { URL u = new URL(url); in = u.openStream(); } catch (Exception e) { in = controller.getResourceAsStream(url); } return loader.load(in); } catch (Exception e) { e.printStackTrace(); } finally { if (in != null) try { in.close(); } catch (Exception ee) { } } return null; } }
I really like the choice for Guice instead of Spring (I dislike code-in-XML, so… 😉
The next step I assume would be to include JUEL or MVEL so the expression looks like:
‘Click Me ${controller.person.name}!’
But then you would lose the compile time checking. You could also simply use the real business model object, like:
‘Click Me ‘ + controller.getPerson().getName() + ‘!’
Why the additional step over getPersonName()? Seems a very good example of the bloat Java code often is accused of. (Assuming there would be more getPersonX() methods.)
Right, the expression language is just the JSP/JSF unified expression language which I understand JUEL is simply an implementation of, with extensions. We’re going to be based on the implementation shipping with GlassFish for obvious reasons (the guy who maintains it is downstairs and across the hall from where I sit :-)). Then you’ll be able to do nice binding like you show, or invoke registered methods, dig into maps and lists, and all the rest.
I’m not too worried about the type checking because, if you use a visual builder like Scene Builder, we’ll handle all that for you. The UEL spec has all kinds of tools support for type-safe code completion. It is likely that NetBeans for example will have very good code completion support so that any errors will be identified in the tool. True, you don’t have the compiler to bail you out, but that’s about it.
On the other hand, moving all the view logic to the controller means that if you have an alternate view that wants to present some other message, based on the same person object, then you have to muddy up the controller with multiple view-specific bindings or property constructions. Maybe in reality that is inevitable, but I hope not.
The getPersonName() would have been getPerson() and personProperty(), but I wanted to keep the code simpler in the example, especially since the bindings don’t work. If the bindings worked, I would have used the properties. Heck, if the bindings worked I would have a much more complicated form with name, address, phone number fields all bound back to the Person with a Submit button :-).
This is all very interesting and exciting. I just hope that we will have the opportunity to play with a beta of the enhanced FXML framework and also the Scene Builder as soon as possible 🙂
A little note:
The comment on the load method of the GuiceFXMLLoader is wrong.
// Load some FXML file, using the supplied Controller, and return the
// instance of the initialized controller…?
The load method does NOT return a controller instance.
One more note:
Why did you annotate the constructor of your GuiceFXMLLoader if you actually call it the old way in your SampleApp? Would it not have been better to add the GuiceFXMLLoader to the Module and then create it this way:
// GuiceFXMLLoader loader = new GuiceFXMLLoader(injector);
GuiceFXMLLoader loader = injector.getInstance(GuiceFXMLLoader.class);
Richard,
I am extremely happy with where you are going with this and love the fact that you are taking the time to explore this side of things.
I like Guice very much, but Spring is still king in JEE (it has many other benefits beyond just DI) so just for reference, I have done a direct port of your above code into the Spring equivalent. There is ZERO spring XML in this – it is all done via annotations and is practically identical to the Guice style. I agree with tbee that code in XML is horrid (FXML being an exception :)) but Spring has had a fully annotation-based alternative for a while that works beautifully with JFX2.
In the Spring equivalent, your Guice ‘Module’ is replaced by an annotated configuration class:
http://code.google.com/p/jfxee/source/browse/trunk/jfxspring/src/main/java/com/zenjava/jfxspring/SampleAppFactory.java
And the controller changes only slightly:
http://code.google.com/p/jfxee/source/browse/trunk/jfxspring/src/main/java/com/zenjava/jfxspring/SampleController.java
Your GuiceFxmlLoader is replaced by a Spring equivalent:
http://code.google.com/p/jfxee/source/browse/trunk/jfxspring/src/main/java/com/zenjava/jfxspring/SpringFxmlLoader.java
And your Application (startup) class has only a minor tweak:
http://code.google.com/p/jfxee/source/browse/trunk/jfxspring/src/main/java/com/zenjava/jfxspring/SampleApp.java
The full working code base is found here (Maven build – I haven’t used ant in years so this is just easier for me):
http://code.google.com/p/jfxee/source/browse/trunk/jfxspring/
I would very much like to take this idea forward and add to it, if you are interested? You have almost exactly implemented the first 20% of the framework I have just been custom building to bang JFX2 into my JEE-style app. It’s a great start but I reckon we can do even better if the time and energy is there. I would love to explore better options and share what I have come up with so far (and others may have further ideas), Are you interested in seeing this at all, and if so what’s the best way to do this (forum, blog, svn, etc)?
As an example of what I mean, I have made the controllers own their ‘root’ node, and the bean factory returns the controller only (the root node is accessible via the controller) – views are dumb presentation bits only, controllers are the fundamental building blocks of the system (and this is now allowing me to build ‘pages’ and ‘wizards’ plus more in a cleanish way).
I have then been able to do away with the ‘SpringFxmlLoader’ and instead the AppFactory does this directly and the bean is magically injected all under the covers and invisible to the outside code (in this case the ‘SampleApp’) – this means the outside code doesn’t know or care if the controller is using an FXML view or a java class plus several other benefits that I can explain/show in more detail if there is interest in this.
I am very keen to contribute what knowledge I’ve got in this space. If it means JEE style apps get better support in JFX as a result well that’s something that could make my life easier for the next 10 years (based on Swing and the last 10 years!). JFX2 is my ticket to never having to build another bloody AJAX-style webapp again!
Cheers,
zonski
For anyone that is interested, I have started a blog with my ideas and thoughts on this topic:
http://www.zenjava.com/
Two posts added so far:
http://www.zenjava.com/2011/10/23/javafx-2-0-fxml-and-spring/
http://www.zenjava.com/2011/10/23/better-controller-injection/
I’m planning to step through the stuff I have come up with in this area, partly as a guide for anyone else doing the same and partly as a conversation point for getting ideas and suggested improvements.
If you wanna get really fancy with Guice, you can inject the controller directly by making it a type parameter of your GuiceFXMLLoader:
public class GuiceFXMLLoader {
@Inject Provider controllerProvider;
public Parent load(String url) {
T instance = controllerProvider.get();
FXMLLoader loader = new FXMLLoader();
loader.getNamespace().put(“controller”, instance);
…
}
}
And then in your calling code:
public class SampleApp extends Application {
@Inject GuiceFXMLLoader controllerLoader;
@Override public void start(Stage stage) throws Exception {
Injector injector = Guice.createInjector(new Module());
injector.injectMembers(this);
Parent root = controllerLoader.load(“Sample.fxml”);
…
}
}
There’s a lot more runway to make this integration really tight. For example, this is how servlet integration works:
new ServletModule() {
@Override protected void configureServlets() {
serve(“*.html”).with(MyServlet.class);
}
}
Cheers,
Jesse
Richard, this is ok, but how can I use @FXML annotation to bind a FXML component (like a TextField or a Button) to a variable if I don’t have the controller specified on the FXML file?
Hi Victor,
Check out this post for details on doing that http://www.zenjava.com/2011/10/23/better-controller-injection/ (it’s using Spring but the same technique should work with Guice).
Cheers,
zonski
Hi,
I’ve been using flex with the swiz framework for a couple of years and when I started using javafx 2.0 I decided I only wanted dependency injection and mvc style event dispatch/handling such as in swiz rather than a whole framework.
Using annotations I’ve defined @Inject, @EventHandler, @Dispatcher and I have a bean loader class with loads some beans at startup but also provides a registerBean method for registering controllers of fxml files in the their constructor method.
The bean loader class also wires the beans together using reflection for the @Inject and @Dispatcher annotations.
Event handling is done using a dispatcher class which again uses reflection to determine which of the loaded beans are handlers for a particular type of event.
There’s a bit of asynchronous code so that one event can be received by a number of handlers operating in different threads.
It’s pretty crude (took about 4 – 6 hours to code) but allows me to think mvc when coding and tie fxml to beans just in time. The one issue that’s bothering me is the lack of callback functions in java (passing a function as an argument which is later called from another bean invoking it in the original class) means that I can’t define a clean helper class like the ServiceHelper in swiz for structuring asynchronous service calls from controllers/delegates
If anyone knows of a way of anything that would help with this part I would be very grateful and of course the code I’ve written for what it’s worth I can make available.