Guice Dependency Injection with Commons Configuration

Problem

It's often necessary to have several configurable parameters on a constructor for a class. Instantiating these classes can be a painful exercise in remembering the order of the parameters and the properly passing them down from parent classes.

Commons Configuration

Commons Configuration solves the problem of passing several primitive parameters to a class. Instead, of passing each primitive, simply pass a Configuration object and pull the primitives one-by-one.

Consider the following example:

public class DoSomething{
  public DoSomething(String user,String environment){
  }
}

In the above example, it's necessary to change the signature when another parameter needs to be passed into the class. Thus, all client code using the constructor has to change as well.

Consider the additional code:

public class DoSomething{
  final public static String CONFIG_CURRENT_USER="Something.CurrentUser";
  final public static String CONFIG_ENVIRONMENT="Something.Environment";
 
  public DoSomething(Configuration config){
    this(config.getString(CONFIG_CURRENT_USER),config.getString(CONFIG_ENVIRONMENT));
  }
  public DoSomething(String user,String environment){
  }
}

Now, all the existing client code can remain intact, and a Configuration object can be passed to the new constructor.

Integration with Guice

Guice now becomes very useful. Generally speaking, injecting primitive types onto a constructor is not Guice's strength. However, now that the constructor only needs to inject a Configuration object, leveraging Guice just means adding an Annotation like this:

public class DoSomething{
 ...
  @Inject
  public DoSomething(Configuration config){
    this(config.getString(CONFIG_CURRENT_USER),config.getString(CONFIG_ENVIRONMENT));
  }
 ...
}

Then, just create an injector in the client code:

     Properties props=new Properties();
        props.load(ClassLoader.getSystemResourceAsStream("app.properties"));
        URL propsUrl=ClassLoader.getSystemResource("app.properties").toURI().toURL();
 
        final Configuration config=new PropertiesConfiguration(propsUrl);
 
        Injector injector=Guice.createInjector(new Module() {
            public void configure(Binder binder) {
                binder.bind(Configuration.class).toInstance(config);
            }
        });
 
        DoSomething something=injector.getInstance(DoSomething.class);

It is definitely a lot of client code, but really only needs to happen once for the entire project. JUnit tests can use the MapConfiguration to define the parameters directly in the code.

No votes yet