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 up all duplicate Integer
s together.
Let's create a module with several duplicate bindings for 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 Integer
instance:
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
.
If we tried to create an Injector
using only the 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 into a Set<T>
binding, which is a union of duplicate sets.
To do this you need to use the multibindToSet(...)
method of ModuleBuilder
DSL.
Let's create 2 modules that define bindings for Set<Integer>
:
Key<Set<Integer>> setKey = new Key<>() {};
Module module1 = ModuleBuilder.create()
.bind(setKey).toInstance(Set.of(1, 2, 3))
.build();
Module module2 = ModuleBuilder.create()
.bind(setKey).toInstance(Set.of(3, 4, 5))
.build();
A module1
defines a binding for the Set<Integer>
which contains elements [1, 2, 3]
. module2
defines a binding
for the Set<Integer>
which 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 the keys and values of all other maps. This is similar to resolving Set<T>
with the exception that
conflicting maps cannot contain the same keys.
To enable Map<K, V>
binding resolution, you must use the multibindToMap(...)
method of ModuleBuilder
DSL.
Let's create 2 modules defininig bindings for Map<Integer, String>
:
Key<Map<Integer, String>> mapKey = new Key<>() {};
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 do 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 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 which can be used instead of @Provides
annotation.
This annotation on a method with the return type T
creates a Set<T>
binding and transforms the provided T
instance
into a Set<T>
containing a single [T]
element.
With@ProvidesIntoSet
annotation, all duplicate Set<T>
bindings are resolved into a set, which is the union
of all other provided sets of type T
.
Let's define three modules that provide Integer
s using methods marked with @ProvidesIntoSet` annotation:
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 three modules and obtain an instance of 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 we requested an instance of Set<Integer>
from the Injector
, we received a set containing elements [1, 2, 3]
.
You can find example sources on GitHub