Featured Post

Applying Email Validation to a JavaFX TextField Using Binding

This example uses the same controller as in a previous post but adds a use case to support email validation.  A Commons Validator object is ...

Thursday, June 11, 2015

Configuring Google Guice

This is a simple post on configuring Google Guice.  It's referenced in some later posts that are more interesting.
Google Guice is a dependency injection framework used to wire application.

Maven

To start using Google Guice with Maven, add the following dependency to your POM.

<dependency>
   <groupid>com.google.inject</groupid>
   <artifactid>guice</artifactid>
   <version>4.0</version>
</dependency>

Guice Module

Next, subclass an AbstractModule with your dependencies. For example, this file configures some constants, a pair of DAOs, and some JavaFX objects.

package com.bekwam.examples.javafx.oldscores1;

import com.google.inject.AbstractModule;
import com.google.inject.name.Names;
import javafx.fxml.JavaFXBuilderFactory;
import javafx.util.BuilderFactory;

public class OldScoresModule extends AbstractModule {

    final private static String MATH_RECENTERED_JSON_FILE = "/data/mathRecentered.json";
    final private static String VERBAL_RECENTERED_JSON_FILE = "/data/verbalRecentered.json";
    final private static String SETTINGS_FILE_NAME = ".oldscores";

    @Override
    protected void configure() {

        String mathRecenteredJSONFile = MATH_RECENTERED_JSON_FILE;
        String verbalRecenteredJSONFile = VERBAL_RECENTERED_JSON_FILE;
        String settingsFileName = SETTINGS_FILE_NAME;

        bind(BuilderFactory.class).to(JavaFXBuilderFactory.class);

        bind(String.class).annotatedWith(Names.named("mathRecenteredJSONFile")).toInstance(mathRecenteredJSONFile);
        bind(String.class).annotatedWith(Names.named("verbalRecenteredJSONFile")).toInstance(verbalRecenteredJSONFile);
        bind(String.class).annotatedWith(Names.named("settingsFileName")).toInstance(settingsFileName);

        bind(SettingsDAO.class).to(SettingsDAOImpl.class).asEagerSingleton();
        bind(RecenteredDAO.class).to(RecenteredDAOImpl.class).asEagerSingleton();
    }
}

Initialize Guice

To start the dependency injection, create a Guice Injector in your main entry point. This example is a JavaFX application that creates an Injector.

    @Override
    public void start(Stage primaryStage) throws Exception {

        if( logger.isDebugEnabled() ) {
            logger.debug("[START]");
        }

        Injector injector = Guice.createInjector(new OldScoresModule());
        
        // continue with start()

   }

Apply to Objects - Direct Call

Your Injector provides you with a factory to create Guice-enabled objects. This example uses Guice to return a JavaFX BuilderFactory configured in OldScoresModule to return a JavaFXBuilderFactory object. GuiceControlFactory is a class that doesn't require an explicit binding. Guice understands the to get() a GuiceControlFactory means to return an object of that class.

        
         FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml1/MainView.fxml"),
                                            null,
                                            injector.getInstance(BuilderFactory.class),
                                            injector.getInstance(GuiceControllerFactory.class));

Apply to Objects - @Inject

Generally, you should wire your objects in this manner: using the @Inject annotation with an optional @Named to differentiate similarly-typed objects. This snippet is a member definition in a called MainViewController which is instantiated by the Guice-backed JavaFX ControllerFactory.

    @Inject
    private SettingsDAO settingsDAO;

Another example adds the @Named annotation. There are several String objects loaded by Guice and Guice cannot differentiate them based on type alone.

    @Inject
    @Named("mathRecenteredJSONFile")
    private String mathRecenteredJSONFile;

Both of the injections produce objects that can be used throughout the classes' methods as they are available after the object is constructed. A special provision is needed if a constructor requires the value.

JavaFX Integration

When you're using FXML, FXMLLoader needs a factory to produce objects. Google Guice can be this factory. All you have to do is provide JavaFX with a handle to an Injector. In "Apply to Objects - Direct Call", you saw the Injector getting a Guice-enabled controller factory. This is the class referenced in that section. Nothing needs to appear in the Guice AbstractModule.

package com.bekwam.javafx.guice;

import com.google.inject.Injector;
import javafx.util.Callback;

import javax.inject.Inject;

public class GuiceControllerFactory implements Callback, Object> {

    @Inject
    Injector injector;

    @Override
    public Object call(Class param) {
        return injector.getInstance(param);
    }
}

Very simple

Dependency frameworks are important and can be deployed with a minimal footprint.  Guice is a 700k pair of JARs (including the javax.inject annotations).  Compared with other frameworks, that's a low download time, small diskspace usage, and reduced amount of JNLP and code signing.

No comments:

Post a Comment