Extension for binding multiple instances in a collection with Set, Map, and Optional binding capabilities.
npx @tessl/cli install tessl/maven-com-google-inject-extensions--guice-multibindings@4.2.0Google Guice Multibindings is a dependency injection extension that enables flexible plugin-style architectures by allowing multiple modules to contribute implementations to a single collection. It provides three key binding mechanisms: Multibinder for adding multiple implementations to a Set, MapBinder for adding key-value pairs to a Map, and OptionalBinder for optional binding capabilities with default value support.
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-multibindings</artifactId>
<version>4.2.3</version>
</dependency>import com.google.inject.multibindings.Multibinder;
import com.google.inject.multibindings.MapBinder;
import com.google.inject.multibindings.OptionalBinder;For provider method annotations:
import com.google.inject.multibindings.ProvidesIntoSet;
import com.google.inject.multibindings.ProvidesIntoMap;
import com.google.inject.multibindings.ProvidesIntoOptional;
import com.google.inject.multibindings.StringMapKey;
import com.google.inject.multibindings.ClassMapKey;
import com.google.inject.multibindings.MapKey;For binding introspection:
import com.google.inject.multibindings.MultibinderBinding;
import com.google.inject.multibindings.MapBinderBinding;
import com.google.inject.multibindings.OptionalBinderBinding;
import com.google.inject.multibindings.MultibindingsTargetVisitor;import com.google.inject.AbstractModule;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.multibindings.MapBinder;
import com.google.inject.multibindings.OptionalBinder;
public class SnacksModule extends AbstractModule {
@Override
protected void configure() {
// Set binding
Multibinder<Snack> multibinder =
Multibinder.newSetBinder(binder(), Snack.class);
multibinder.addBinding().to(Twix.class);
multibinder.addBinding().to(Snickers.class);
// Map binding
MapBinder<String, Snack> mapbinder =
MapBinder.newMapBinder(binder(), String.class, Snack.class);
mapbinder.addBinding("twix").to(Twix.class);
mapbinder.addBinding("snickers").to(Snickers.class);
// Optional binding
OptionalBinder<Renamer> optionalBinder =
OptionalBinder.newOptionalBinder(binder(), Renamer.class);
optionalBinder.setDefault().to(DefaultRenamer.class);
}
}
// Usage with provider methods
public class ProvidersModule extends AbstractModule {
@ProvidesIntoSet
Snack provideChocolate() { return new Chocolate(); }
@ProvidesIntoMap
@StringMapKey("sweet")
Snack provideSweetSnack() { return new Candy(); }
@ProvidesIntoOptional(ProvidesIntoOptional.Type.DEFAULT)
Logger provideDefaultLogger() { return new ConsoleLogger(); }
}Guice Multibindings is built around three core concepts:
The API supports both imperative binding (using binder methods in configure()) and declarative binding (using @Provides annotations with special multibinding annotations).
Creates collections where multiple modules can contribute elements to a single Set. Ideal for plugin architectures, event handlers, and extensible service registries.
public static <T> Multibinder<T> newSetBinder(Binder binder, Class<T> type);
public static <T> Multibinder<T> newSetBinder(Binder binder, TypeLiteral<T> type);
public static <T> Multibinder<T> newSetBinder(Binder binder, Class<T> type, Annotation annotation);
public static <T> Multibinder<T> newSetBinder(Binder binder, TypeLiteral<T> type, Annotation annotation);
public static <T> Multibinder<T> newSetBinder(Binder binder, Class<T> type, Class<? extends Annotation> annotationType);
public static <T> Multibinder<T> newSetBinder(Binder binder, TypeLiteral<T> type, Class<? extends Annotation> annotationType);
public static <T> Multibinder<T> newSetBinder(Binder binder, Key<T> key);
public Multibinder<T> permitDuplicates();
public LinkedBindingBuilder<T> addBinding();Creates key-value collections where multiple modules can contribute entries to a single Map. Perfect for registries, configuration systems, and named service lookups.
public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, Class<K> keyType, Class<V> valueType);
public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType);
public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, Class<K> keyType, Class<V> valueType, Annotation annotation);
public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType, Annotation annotation);
public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, Class<K> keyType, Class<V> valueType, Class<? extends Annotation> annotationType);
public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType, Class<? extends Annotation> annotationType);
public MapBinder<K, V> permitDuplicates();
public LinkedBindingBuilder<V> addBinding(K key);Creates optional dependencies with default value support. Allows frameworks to define extension points that users can optionally override.
public static <T> OptionalBinder<T> newOptionalBinder(Binder binder, Class<T> type);
public static <T> OptionalBinder<T> newOptionalBinder(Binder binder, TypeLiteral<T> type);
public static <T> OptionalBinder<T> newOptionalBinder(Binder binder, Key<T> type);
public LinkedBindingBuilder<T> setDefault();
public LinkedBindingBuilder<T> setBinding();Enables advanced introspection of multibinding configurations using the visitor pattern. Useful for tools, debuggers, and frameworks that need to analyze binding structure.
public interface MultibindingsTargetVisitor<T, V> extends BindingTargetVisitor<T, V> {
/** Visits a binding created through Multibinder. */
V visit(MultibinderBinding<? extends T> multibinding);
/** Visits a binding created through MapBinder. */
V visit(MapBinderBinding<? extends T> mapbinding);
/** Visits a binding created through OptionalBinder. @since 4.0 */
V visit(OptionalBinderBinding<? extends T> optionalbinding);
}// Annotation for contributing to Sets via provider methods
@Target(METHOD)
@Retention(RUNTIME)
public @interface ProvidesIntoSet {}
// Annotation for contributing to Maps via provider methods
@Target(METHOD)
@Retention(RUNTIME)
public @interface ProvidesIntoMap {}
// Annotation for contributing to Optionals via provider methods
@Target(METHOD)
@Retention(RUNTIME)
public @interface ProvidesIntoOptional {
enum Type { ACTUAL, DEFAULT }
Type value();
}
// Meta-annotation for creating custom map key annotations
@Target(ANNOTATION_TYPE)
@Retention(RUNTIME)
public @interface MapKey {
boolean unwrapValue() default true;
}
// Built-in map key annotation for String keys
@MapKey(unwrapValue = true)
@Target(METHOD)
@Retention(RUNTIME)
public @interface StringMapKey {
String value();
}
// Built-in map key annotation for Class keys
@MapKey(unwrapValue = true)
@Target(METHOD)
@Retention(RUNTIME)
public @interface ClassMapKey {
Class<?> value();
}
// Binding information interfaces for introspection
public interface MultibinderBinding<T> {
Key<T> getSetKey();
Set<Key<?>> getAlternateSetKeys();
TypeLiteral<?> getElementTypeLiteral();
List<Binding<?>> getElements();
boolean permitsDuplicates();
boolean containsElement(Element element);
}
public interface MapBinderBinding<T> {
Key<T> getMapKey();
Set<Key<?>> getAlternateMapKeys();
TypeLiteral<?> getKeyTypeLiteral();
TypeLiteral<?> getValueTypeLiteral();
List<Map.Entry<?, Binding<?>>> getEntries();
List<Map.Entry<?, Binding<?>>> getEntries(Iterable<? extends Element> elements);
boolean permitsDuplicates();
boolean containsElement(Element element);
}
public interface OptionalBinderBinding<T> {
Key<T> getKey();
Set<Key<?>> getAlternateKeys();
Binding<?> getDefaultBinding();
Binding<?> getActualBinding();
boolean containsElement(Element element);
}