By request, here’s how to do the better controller injection I outlined in an earlier post, but using Guice instead of Spring.
It’s worth noting however, that JavaFX 2.0.2 is targeted for release in about a month or so (or so Richard has alluded to in this forum topic), and that this should have a way to control how FXML instantiates its controllers. This should make it possible to use both Spring and Guice in more natural ways. If you’re reading this and 2.0.2 has been released, you may want to look for a more recent post.
But for now, here’s how it goes. We’ll use Richard’s example as a basis again, merging in the concepts from my previous post.
Person.java and sample.fxml are unchanged from the previous post.
In SampleController.java, we simply revert back to using Guice’s @Inject instead of Spring’s @Autowired.
public class SampleController
{
@FXML private Node view;
@Inject private Person person;
...
}
The biggest change is in our application factory. Instead of our SampleAppFactory, which was essentially a Spring configuration file, we have a Guice module instead, called SampleAppModule.
There are a few different options for loading our controllers in this module. The easiest way is to bind our Controller class directly to an instance, using the toInstance() binding method. This allows us to do the instantiation (or rather allows FXMLLoader to do the instantiation at our request) and then Guice takes care of all the injection from there. If we use this option, our SampleAppModule.java looks like this:
import com.google.inject.AbstractModule;
import javafx.fxml.FXMLLoader;
import java.io.IOException;
import java.io.InputStream;
public class SampleAppModule extends AbstractModule
{
protected void configure()
{
// we can use instance binding for the person object, or if it had an empty
// constructor (as in Richard's example) we could use the default Guice binding
bind(Person.class).toInstance(new Person("Richard"));
// OPTION 1: bind to an instance
bind(SampleController.class).toInstance((SampleController) loadController("/sample.fxml"));
}
protected Object loadController(String url)
{
// normal loading stuff here - see full code for details
}
}
The only limitation with this approach is that all our controllers will be created up front when the application starts. Depending on your requirements, this could be a good thing or it could be a bad thing – basically trading startup load time for in-application load time.
If you don’t want to load everything straight away and need to delay the loading, then the alternative is to use Guice Providers. You can create a whole Provider class if you want, however it is much easier to just annotate a provider method in your module for these simple cases.
There is a drawback to this approach however. Once we’ve told Guice we want to use a provider, Guice no longer injects our dependencies for us. The best we can do is ask Guice to inject our provider method with dependencies and then we have to manually inject these into our created class (this seems a little limited, and perhaps I have missed something in Guice here – if anybody knows a way around this, please post in the comments).
Using the provider option, our SampleAppModule looks like this:
<pre>import com.google.inject.AbstractModule;
import javafx.fxml.FXMLLoader;
import java.io.IOException;
import java.io.InputStream;
public class SampleAppModule extends AbstractModule
{
protected void configure()
{
bind(Person.class).toInstance(new Person("Richard"));
}
// OPTION 2: use a provider - the person parameter is the one bound in the configure() method
@Provides
public SampleController sampleController(Person person)
{
System.out.println("Creating controller");
SampleController controller = (SampleController) loadController("/sample.fxml");
// we have to manually set our person object since Guice has washed its hands of this
// controller leaving it all up to us to create and configure (i.e. 'provide') it
controller.setPerson(person);
return controller;
}
protected Object loadController(String url)
{
// normal loading stuff here - see full code for details
}
}
The full code for this post can be found at: http://code.google.com/p/jfxee/source/browse/trunk/jfxee6/
Continue Reading→
