0
# Map Binding (MapBinder)
1
2
Map binding functionality that allows multiple modules to contribute key-value entries to a single Map collection. Entries are bound individually and then injected as a complete Map, enabling registry-style patterns where different modules can register services, configurations, or handlers by name.
3
4
## Capabilities
5
6
### MapBinder Factory Methods
7
8
Creates new MapBinder instances for different key/value type and annotation combinations.
9
10
```java { .api }
11
/**
12
* Returns a new mapbinder that collects entries of keyType/valueType in a Map
13
* that is itself bound with no binding annotation.
14
*/
15
public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, Class<K> keyType, Class<V> valueType);
16
17
/**
18
* Returns a new mapbinder that collects entries of keyType/valueType in a Map
19
* that is itself bound with no binding annotation.
20
*/
21
public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType);
22
23
/**
24
* Returns a new mapbinder that collects entries of keyType/valueType in a Map
25
* that is itself bound with annotation.
26
*/
27
public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, Class<K> keyType, Class<V> valueType, Annotation annotation);
28
29
/**
30
* Returns a new mapbinder that collects entries of keyType/valueType in a Map
31
* that is itself bound with annotation.
32
*/
33
public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType, Annotation annotation);
34
35
/**
36
* Returns a new mapbinder that collects entries of keyType/valueType in a Map
37
* that is itself bound with annotationType.
38
*/
39
public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, Class<K> keyType, Class<V> valueType, Class<? extends Annotation> annotationType);
40
41
/**
42
* Returns a new mapbinder that collects entries of keyType/valueType in a Map
43
* that is itself bound with annotationType.
44
*/
45
public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType, Class<? extends Annotation> annotationType);
46
```
47
48
### Entry Binding
49
50
Add key-value entries to the Map collection.
51
52
```java { .api }
53
/**
54
* Returns a binding builder used to add a new entry in the map. Each key must be
55
* distinct (and non-null). Bound providers will be evaluated each time the map is injected.
56
*
57
* It is an error to call this method without also calling one of the to methods on the
58
* returned binding builder. Scoping elements independently is supported.
59
*/
60
public LinkedBindingBuilder<V> addBinding(K key);
61
```
62
63
### Duplicate Key Handling
64
65
Configure how duplicate keys are handled.
66
67
```java { .api }
68
/**
69
* Configures the MapBinder to handle duplicate entries.
70
*
71
* When multiple equal keys are bound, the value that gets included in the map is arbitrary.
72
* In addition to the Map<K, V> and Map<K, Provider<V>> maps that are normally bound,
73
* a Map<K, Set<V>> and Map<K, Set<Provider<V>>> are also bound, which contain all values
74
* bound to each key.
75
*
76
* When multiple modules contribute elements to the map, this configuration option impacts all of them.
77
*/
78
public MapBinder<K, V> permitDuplicates();
79
```
80
81
### Provider Method Support
82
83
Contribute entries using provider methods with map-specific annotations.
84
85
```java { .api }
86
/**
87
* Annotates methods of a Module to add items to a MapBinder. The method's return
88
* type, binding annotation and additional key annotation determines what Map this will contribute to.
89
*/
90
@Target(METHOD)
91
@Retention(RUNTIME)
92
public @interface ProvidesIntoMap {}
93
94
/**
95
* Meta-annotation for creating custom map key annotations. When unwrapValue is true,
96
* the value() type will be the key type for injected map and the value() instances
97
* will be the keys values.
98
*/
99
@Target(ANNOTATION_TYPE)
100
@Retention(RUNTIME)
101
public @interface MapKey {
102
boolean unwrapValue() default true;
103
}
104
105
/**
106
* Built-in map key annotation for String keys.
107
*/
108
@MapKey(unwrapValue = true)
109
@Target(METHOD)
110
@Retention(RUNTIME)
111
public @interface StringMapKey {
112
String value();
113
}
114
115
/**
116
* Built-in map key annotation for Class keys.
117
*/
118
@MapKey(unwrapValue = true)
119
@Target(METHOD)
120
@Retention(RUNTIME)
121
public @interface ClassMapKey {
122
Class<?> value();
123
}
124
```
125
126
**Usage Examples:**
127
128
**Basic Map Binding:**
129
130
```java
131
public class SnacksModule extends AbstractModule {
132
@Override
133
protected void configure() {
134
MapBinder<String, Snack> mapbinder =
135
MapBinder.newMapBinder(binder(), String.class, Snack.class);
136
mapbinder.addBinding("twix").toInstance(new Twix());
137
mapbinder.addBinding("snickers").toProvider(SnickersProvider.class);
138
mapbinder.addBinding("skittles").to(Skittles.class);
139
}
140
}
141
142
// Injection
143
@Inject
144
public SnackMachine(Map<String, Snack> snacks) {
145
this.snacks = snacks; // Contains {"twix" -> Twix, "snickers" -> Snickers, "skittles" -> Skittles}
146
}
147
```
148
149
**Provider Map Injection:**
150
151
```java
152
// Can inject Map<K, Provider<V>> for lazy value evaluation
153
@Inject
154
public SnackMachine(Map<String, Provider<Snack>> snackProviders) {
155
this.snackProviders = snackProviders;
156
// Values are created only when Provider.get() is called
157
}
158
```
159
160
**Annotated Map Binding:**
161
162
```java
163
public class HandlersModule extends AbstractModule {
164
@Override
165
protected void configure() {
166
MapBinder<String, RequestHandler> mapbinder = MapBinder.newMapBinder(
167
binder(), String.class, RequestHandler.class, Names.named("web"));
168
mapbinder.addBinding("users").to(UserRequestHandler.class);
169
mapbinder.addBinding("orders").to(OrderRequestHandler.class);
170
}
171
}
172
173
// Injection
174
@Inject
175
public WebServer(@Named("web") Map<String, RequestHandler> handlers) {
176
this.handlers = handlers;
177
}
178
```
179
180
**Provider Method Binding with String Keys:**
181
182
```java
183
public class PluginsModule extends AbstractModule {
184
@ProvidesIntoMap
185
@StringMapKey("user")
186
Plugin provideUserPlugin(UserService userService) {
187
return new UserPlugin(userService);
188
}
189
190
@ProvidesIntoMap
191
@StringMapKey("order")
192
Plugin provideOrderPlugin() {
193
return new OrderPlugin();
194
}
195
196
@ProvidesIntoMap
197
@StringMapKey("notification")
198
@Named("web") // Can combine with binding annotations
199
Plugin provideWebNotificationPlugin() {
200
return new WebNotificationPlugin();
201
}
202
}
203
```
204
205
**Provider Method Binding with Class Keys:**
206
207
```java
208
public class ProcessorsModule extends AbstractModule {
209
@ProvidesIntoMap
210
@ClassMapKey(String.class)
211
Processor provideStringProcessor() {
212
return new StringProcessor();
213
}
214
215
@ProvidesIntoMap
216
@ClassMapKey(Integer.class)
217
Processor provideIntegerProcessor() {
218
return new IntegerProcessor();
219
}
220
}
221
222
// Injection
223
@Inject
224
public ProcessorRegistry(Map<Class<?>, Processor> processors) {
225
this.processors = processors; // {String.class -> StringProcessor, Integer.class -> IntegerProcessor}
226
}
227
```
228
229
**Custom Map Key Annotation:**
230
231
```java
232
// Define custom enum key annotation
233
public enum Environment { DEV, TEST, PROD }
234
235
@MapKey(unwrapValue = true)
236
@Retention(RUNTIME)
237
public @interface EnvironmentKey {
238
Environment value();
239
}
240
241
// Use in provider method
242
@ProvidesIntoMap
243
@EnvironmentKey(Environment.PROD)
244
DatabaseConfig provideProdConfig() {
245
return new ProductionDatabaseConfig();
246
}
247
```
248
249
**Multiple Modules Contributing:**
250
251
```java
252
// CandyModule
253
public class CandyModule extends AbstractModule {
254
@Override
255
protected void configure() {
256
MapBinder<String, Snack> mapbinder =
257
MapBinder.newMapBinder(binder(), String.class, Snack.class);
258
mapbinder.addBinding("chocolate").to(Chocolate.class);
259
mapbinder.addBinding("gummies").to(Gummies.class);
260
}
261
}
262
263
// ChipsModule
264
public class ChipsModule extends AbstractModule {
265
@Override
266
protected void configure() {
267
MapBinder<String, Snack> mapbinder =
268
MapBinder.newMapBinder(binder(), String.class, Snack.class);
269
mapbinder.addBinding("doritos").to(Doritos.class);
270
mapbinder.addBinding("cheetos").to(Cheetos.class);
271
}
272
}
273
274
// Final injected Map contains entries from both modules
275
```
276
277
**Duplicate Key Handling:**
278
279
```java
280
public class ConfigModule extends AbstractModule {
281
@Override
282
protected void configure() {
283
MapBinder<String, String> mapbinder = MapBinder.newMapBinder(binder(), String.class, String.class)
284
.permitDuplicates(); // Enable duplicate key support
285
mapbinder.addBinding("env").toInstance("dev");
286
mapbinder.addBinding("env").toInstance("test"); // Would normally fail without permitDuplicates()
287
}
288
}
289
290
// With permitDuplicates(), additional Map types are bound:
291
@Inject Map<String, Set<String>> multiValueMap; // {"env" -> {"dev", "test"}}
292
@Inject Map<String, Set<Provider<String>>> multiProviderMap;
293
```
294
295
## Key Features
296
297
- **Key Uniqueness**: By default, keys must be distinct or injection will fail
298
- **Iteration Order**: Map iteration order is consistent with binding order within a module
299
- **Multi-Module Support**: Multiple modules can contribute to the same Map
300
- **Provider Support**: Can inject `Map<K, Provider<V>>` for lazy value evaluation
301
- **Scoping**: Individual values can be scoped independently
302
- **Annotation Support**: Different Maps of same key/value type can be created using binding annotations
303
- **Duplicate Key Support**: When enabled, provides additional `Map<K, Set<V>>` bindings
304
- **Custom Key Types**: Extensible key annotation system supports any serializable type