Tuesday, December 16, 2008

Private Bindings in Guice

There are rare occasions when I need to bind something locally in Guice, but I don't want that binding exposed. One such time happened today. Our application uses a Configuration object to hold what we read in from a configuration file. There is plenty of configuration in that file including database connection information and email configuration for reporting errors. Various parts of the application need various parts of the configuration, but nothing really needs the whole ball of wax.

There is, however, one exception to this in our application where a Guice Provider actually does need to have the whole Configuration object. So, we need to have a singleton Configuration instance bound, but I still don't want that exposed.

Enter annotated bindings. Guice allows you to bind the same type more than once and to distinguish the different bindings by adding an annotation. The annotation gets added to the binding in the module as well as the location where the injection is to occur. So, in this case, we have a binding and Provider like so:


bind(Configuration.class).annotatedWith(DontExpose.class).toInstance(privateConfig);
bind(Foo.class).toProvider(FooProvider.class);

private static class FooProvider implements Provider {
public FooProvider(@DontExpose Configuration config) {
...
}
}


The binding is placed in the configure() method of the Guide Module, and the FooProvider is a private static within the same Guice Module class. Unless you take one extra step, however, this binding is still exposed.


Normally, my binding annotations are publicly defined within the project. Any class that Guice builds can ask for a Configuration annotated with @DontExpose. To keep that from happening, we simply define the @DontExpose binding annotation as a private element within the Guice Module. So, the final module would look something like this:


import com.google.inject.*;
import java.lang.annotation.*;
import org.apache.commons.configuration.*;

public class TestModuleShouldBeDeleted extends AbstractModule {

private final CompositeConfiguration config;

public TestModuleShouldBeDeleted(CompositeConfiguration config) {
this.config = config;
}

@Override
protected void configure() {
bind(Configuration.class).annotatedWith(DontExpose.class).toInstance(config);
bind(Foo.class).toProvider(FooProvider.class);
}

@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.FIELD, ElementType.PARAMETER })
@BindingAnnotation
private @interface DontExpose {
// marker annotation
}

private static class FooProvider implements Provider {

private final Configuration configuration;

@Inject
public FooProvider(@DontExpose
Configuration configuration) {
this.configuration = configuration;
}

@Override
public Foo get() {
return new Foo(configuration.getString("foo.value"));
}
}
}


Now, the binding to Foo is exposed (as we want it to be), but the binding to the Configuration is not. This isn't a pattern I use very often, but it was useful today.

No comments: