Resolving duplicate bindings
In this tutorial we will look at resolution of duplicate bindings using Multibinders.
Defining a multibinder
We can define multibinders to be exported from a module.
Module multibinderModule = ModuleBuilder.create() .multibind(Key.of(Integer.class), Multibinders.ofBinaryOperator(Integer::sum)) .build();
Here we have defined a multibinder for Integer
s that simply adds all of the duplicate Integer
s together.
Let's create a module with multiple duplicate bindings for an Integer
:
Module integersModule = ModuleBuilder.create() .bind(Integer.class).toInstance(1) .bind(Integer.class).toInstance(10) .bind(Integer.class).toInstance(100) .build();
Now let's create an Injector
from these two modules and obtain an instance of an Integer
:
Injector injector = Injector.of(multibinderModule, integersModule);System.out.println(injector.getInstance(Integer.class));
The result is 111
as the sum of 1
, 10
and 100
.
note
If we would try to create an Injector
using only an integersModule
without specifying a multibinder, we would get a
Duplicate bindings
error.
You can find example sources on GitHub
Resolution of Set<T>
bindings
When you have multiple duplicate bindings for Set<T>
you can automatically resolve them to a Set<T>
binding that is a union of duplicate sets.
To do this you need to use multibindToSet(...)
method of ModuleBuilder
DSL.
Let's create 2 modules that define bindings for Set<Integer>
:
Key<Set<Integer>> setKey = new Key<Set<Integer>>() {};
Module module1 = ModuleBuilder.create() .bind(setKey).toInstance(new HashSet<>(Arrays.asList(1, 2, 3))) .build();Module module2 = ModuleBuilder.create() .bind(setKey).toInstance(new HashSet<>(Arrays.asList(3, 4, 5))) .build();
A module1
defines a binding for a Set<Integer>
that contains elements [1, 2, 3]
. While module2
defines a binding
for a Set<Integer>
that contains elements [3, 4, 5]
.
Now let's create a module with a multibinder using ModuleBuilder#multibindToSet
:
Module multibinderModule = ModuleBuilder.create() .multibindToSet(Integer.class) .build();
Finally, let's create an Injector
from modules and obtain an instance of a Set<Integer>
:
Injector injector = Injector.of(module1, module2, multibinderModule);System.out.println(injector.getInstance(setKey));
Obtained Set<Integer>
contains a union of both sets: [1, 2, 3, 4, 5]
.
You can find example sources on GitHub
Resolution of Map<K, V>
bindings
When you have multiple duplicate bindings for Map<K, V>
you can automatically resolve them to a Map<K, V>
binding that
contains a union of keys and values of all the other maps. This is similar to resolution of Set<T>
with an exception that
conflicting maps cannot contain same keys.
To enable resolution of Map<K, V>
bindings you nned to use multibindToMap(...)
method of ModuleBuilder
DSL.
Let's create 2 modules that define bindings for Map<Integer, String>
:
Key<Map<Integer, String>> mapKey = new Key<Map<Integer, String>>() {};
Module module1 = ModuleBuilder.create() .bind(mapKey).to(() -> { Map<Integer, String> map = new HashMap<>(); map.put(1, "one"); map.put(2, "two"); map.put(3, "three"); return map; }) .build();
Module module2 = ModuleBuilder.create() .bind(mapKey).to(() -> { Map<Integer, String> map = new HashMap<>(); map.put(4, "four"); map.put(5, "five"); map.put(6, "six"); return map; }) .build();
A module1
defines a binding for a Map<Integer, String>
that contains entries [1="one", 2="two", 3="three"]
. While module2
defines a binding
for a Map<Integer, String>
that contains entries [4="four", 5="five", 6="six"]
. Notice, that maps does not contain same keys.
Now let's create a module with a multibinder using ModuleBuilder#multibindToMap
:
Module multibinderModule = ModuleBuilder.create() .multibindToMap(Integer.class, String.class) .build();
Finally, let's create an Injector
from modules and obtain an instance of a Map<Integer, String>
:
Injector injector = Injector.of(module1, module2, multibinderModule);System.out.println(injector.getInstance(mapKey));
Obtained Map<Integer, String>
is a merged map that contains all of the entries of conflicting maps:
[1="one", 2="two", 3="three", 4="four", 5="five", 6="six"]
.
You can find example sources on GitHub
@ProvidesIntoSet
annotation
ActiveJ Inject supports a special @ProvidesIntoSet
annotation that can be used in place of @Provides
annotation.
This annotation on a method with a return type T
creates a binding of Set<T>
and transforms a provided instance of T
into a Set<T>
that contains a single element [T]
.
When @ProvidesIntoSet
annotation is used all the duplicate Set<T>
bindings a resolved to a set that is a union
of all the other provided sets of type T
.
Let's define three modules that provide Integer
s using methods marked with @ProvidesIntoSet` annotations:
public static final class MyModule1 extends AbstractModule { @ProvidesIntoSet Integer integer() { return 1; }}
public static final class MyModule2 extends AbstractModule { @ProvidesIntoSet Integer integer() { return 2; }}
public static final class MyModule3 extends AbstractModule { @ProvidesIntoSet Integer integer() { return 3; }}
Finally, let's create an Injector
from those 3 modules and obtain an instance of a Set<Integer>
:
Module module1 = new MyModule1();Module module2 = new MyModule2();Module module3 = new MyModule3();Injector injector = Injector.of(module1, module2, module3);System.out.println(injector.getInstance(new Key<Set<Integer>>() {}));
When requesting an instance of Set<Integer>
from the Injector
we received a set containing elements [1, 2, 3]
.
You can find example sources on GitHub