Guice Stories — Part 2

Andrus Adamchik
5 min readMay 18, 2017

--

Part 1 of the Guice Stories was published here. Continuing with more stories.

Story 3: Filling Maps and Collections

Previously we saw the examples of adding objects to MapBinder. Let’s dwell on this for a moment. Here I will use Multibinder for a change (remember, it produces an injectable Set<SomeType>), but the discussion applies to MapBinder just as well. What can we put in these DI-managed collections? If the objects in the Set are trivial to create and do not rely on any dependencies, we can add them as instances:

public void configure(Binder binder) {   Multibinder.newSetBinder(binder, String.class)
.addBinding()
.toInstance("value");
}

Though most objects (like our commands from the previous story) do require assembly and/or dependencies . So one option is to add them by type (just like we did with commands):

@Override
public void configure(Binder binder) {
Multibinder.newSetBinder(binder, MyType.class)
.addBinding()
.to(MySubType.class);
}

Intuitively it is clear what happens here (an instance of MySubType is created and loaded into Set<MyType>). What’s important to understand is that adding to collections is a form of injection. I.e. MySubtype.class is a shortcut for a “key” to lookup a DI binding. So while it may appear that we are telling Guice to “create an instance of MySubtype.class”, in fact we are asking to “find an injectable object of this type, regardless of how it was created”. What are we to do with this subtle knowledge? For one thing we can customize MySubType assembly code via a dedicated provider method. E.g.:

@Provides
@Singleton
MySubType provideMySubtype() {
return new MySubType("provided_value");
}

Another common scenario —filling collection with multiple instances of the same type that differ only in their generic parameter. E.g. consider the following class:

public class MyGenericType<T> {   private T value;   public MyGenericType(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}

Say we want to build a Set<MyGenericType>. We can’t use the approach above. Due to Java type erasure we can’t obtain Class objects that correspond specifically to MyGenericType<String> or MyGenericType<Integer>. The most commonly used trick to work around this problem is to create an anonymous inner class that mirrors the generics signature of the desired type. Such class can be used as a binding key. In Guice there is a superclass for it called TypeLiteral:

@Override
public void configure(Binder binder) {
TypeLiteral<MyGenericType<String>> stringType =
new TypeLiteral<MyGenericType<String>>() {};
Multibinder.newSetBinder(binder, MyGenericType.class)
.addBinding()
.to(stringType);
TypeLiteral<MyGenericType<Integer>> intType =
new TypeLiteral<MyGenericType<Integer>>() {};
Multibinder.newSetBinder(binder, MyGenericType.class)
.addBinding()
.to(intType);
}
@Singleton
@Provides
MyGenericType<Integer> provideIntType() {
return new MyGenericType<>(5);
}
@Singleton
@Provides
MyGenericType<String> provideStringType() {
return new MyGenericType<>("string");
}

Notice that we only need a proper TypeLiteral when adding an object to a collection. Generic types of the provider methods are resolved by Guice automatically.

Story 4: Going in Circles

A common problem that all Guice (or more generally — DI) users will run into sooner or later is dependency cycles. E.g. class A depends on class B, B depends on C, C depends on A. Can a DI container still create all these objects for us?

In most cases the answer is “yes”. Guice will figure out how to correctly resolve a cycle and you won’t even know you had a potential problem. But there are cases when it won’t. Consider this example using constructor injection:

public class A {   private B b;   @Inject
public A(B b) {
this.b = b;
}
public B getB() {
return b;
}
}
public class B { private C c; @Inject
public B(C c) {
this.c = c;
}
public C getC() {
return c;
}
}
public class C { private A a; @Inject
public C(A a) {
this.a = a;
}
public A getA() {
return a;
}
}

When trying to access an instance of A, you will get a scary error that starts like this:

com.google.inject.ProvisionException: Unable to provision, see the following errors:1) Tried proxying com.foo.A to support a circular dependency, but it is not an interface.

The same issue would have happened with provider methods. But not with field injection! For all its downsides discussed above, field injection would allow Guice to handle dependency cycles with no user interference.

Now let’s look at the exception. It hints that we won’t have to resort to field injection. Instead we can replace direct dependencies on classes with dependencies on interfaces. Then Guice would be able to create a proxy placeholder for the interface object, deferring resolution of the actual object until later. This solution is clean and injection of interfaces may even improve our overall design.

There’s also one more way to break the cycle — injecting Guice Provider instead of the instance of a dependency. Rewriting class C to take a Provider<A> solves the problem just as well:

public static class C {   private Provider<A> aProvider;   @Inject
public C(Provider<A> aProvider) {
this.aProvider = aProvider;
}
public A getA() {
return aProvider.get();
}
}

The trade-off is a newly acquired hard dependency on Guice API, that we tried to avoid so hard in the previous stories. To keep things clean, you may replace Guice Provider with Java Supplier, and use provider method to convert injectable provider into supplier. I am leaving this as an exercise to the reader.

Referencing dependencies via Providers (or Suppliers) may help us beyond just breaking the cycles. Another thing it does is deferring initialization of dependencies, which comes handy when optimizing various conditional execution flows. E.g. consider an app that, depending on the passed command-line flags, either prints a help message or runs a database operation. It would be highly inconvenient to eagerly connect to the database if all we need is to print a help message. Injecting a provider solves this problem.

Conclusion

This concludes the second part of the Guice Stories. I hope someone finds it useful. I have other story ideas. E.g. I’ve been researching lately how to best use Guice with functional Java code and some patterns are starting to emerge. So hopefully I will get around to continue this series. Feel free to suggest your own ideas and useful Guice patterns.

Follow me on Twitter.

--

--

Andrus Adamchik

ObjectStyle ; Open Source: ApacheCayenne, Agrest, Bootique, and more…