CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-google-inject-extensions--guice-multibindings

Extension for binding multiple instances in a collection with Set, Map, and Optional binding capabilities.

Pending
Overview
Eval results
Files

map-binding.mddocs/

Map Binding (MapBinder)

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.

Capabilities

MapBinder Factory Methods

Creates new MapBinder instances for different key/value type and annotation combinations.

/**
 * Returns a new mapbinder that collects entries of keyType/valueType in a Map 
 * that is itself bound with no binding annotation.
 */
public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, Class<K> keyType, Class<V> valueType);

/**
 * Returns a new mapbinder that collects entries of keyType/valueType in a Map 
 * that is itself bound with no binding annotation.
 */
public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType);

/**
 * Returns a new mapbinder that collects entries of keyType/valueType in a Map 
 * that is itself bound with annotation.
 */
public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, Class<K> keyType, Class<V> valueType, Annotation annotation);

/**
 * Returns a new mapbinder that collects entries of keyType/valueType in a Map 
 * that is itself bound with annotation.
 */
public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType, Annotation annotation);

/**
 * Returns a new mapbinder that collects entries of keyType/valueType in a Map 
 * that is itself bound with annotationType.
 */
public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, Class<K> keyType, Class<V> valueType, Class<? extends Annotation> annotationType);

/**
 * Returns a new mapbinder that collects entries of keyType/valueType in a Map 
 * that is itself bound with annotationType.
 */
public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType, Class<? extends Annotation> annotationType);

Entry Binding

Add key-value entries to the Map collection.

/**
 * Returns a binding builder used to add a new entry in the map. Each key must be 
 * distinct (and non-null). Bound providers will be evaluated each time the map is injected.
 * 
 * It is an error to call this method without also calling one of the to methods on the 
 * returned binding builder. Scoping elements independently is supported.
 */
public LinkedBindingBuilder<V> addBinding(K key);

Duplicate Key Handling

Configure how duplicate keys are handled.

/**
 * Configures the MapBinder to handle duplicate entries.
 * 
 * When multiple equal keys are bound, the value that gets included in the map is arbitrary.
 * In addition to the Map<K, V> and Map<K, Provider<V>> maps that are normally bound, 
 * a Map<K, Set<V>> and Map<K, Set<Provider<V>>> are also bound, which contain all values 
 * bound to each key.
 * 
 * When multiple modules contribute elements to the map, this configuration option impacts all of them.
 */
public MapBinder<K, V> permitDuplicates();

Provider Method Support

Contribute entries using provider methods with map-specific annotations.

/**
 * Annotates methods of a Module to add items to a MapBinder. The method's return 
 * type, binding annotation and additional key annotation determines what Map this will contribute to.
 */
@Target(METHOD)
@Retention(RUNTIME)
public @interface ProvidesIntoMap {}

/**
 * Meta-annotation for creating custom map key annotations. When unwrapValue is true, 
 * the value() type will be the key type for injected map and the value() instances 
 * will be the keys values.
 */
@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();
}

Usage Examples:

Basic Map Binding:

public class SnacksModule extends AbstractModule {
  @Override
  protected void configure() {
    MapBinder<String, Snack> mapbinder = 
        MapBinder.newMapBinder(binder(), String.class, Snack.class);
    mapbinder.addBinding("twix").toInstance(new Twix());
    mapbinder.addBinding("snickers").toProvider(SnickersProvider.class);
    mapbinder.addBinding("skittles").to(Skittles.class);
  }
}

// Injection
@Inject
public SnackMachine(Map<String, Snack> snacks) {
  this.snacks = snacks; // Contains {"twix" -> Twix, "snickers" -> Snickers, "skittles" -> Skittles}
}

Provider Map Injection:

// Can inject Map<K, Provider<V>> for lazy value evaluation
@Inject
public SnackMachine(Map<String, Provider<Snack>> snackProviders) {
  this.snackProviders = snackProviders;
  // Values are created only when Provider.get() is called
}

Annotated Map Binding:

public class HandlersModule extends AbstractModule {
  @Override
  protected void configure() {
    MapBinder<String, RequestHandler> mapbinder = MapBinder.newMapBinder(
        binder(), String.class, RequestHandler.class, Names.named("web"));
    mapbinder.addBinding("users").to(UserRequestHandler.class);
    mapbinder.addBinding("orders").to(OrderRequestHandler.class);
  }
}

// Injection
@Inject
public WebServer(@Named("web") Map<String, RequestHandler> handlers) {
  this.handlers = handlers;
}

Provider Method Binding with String Keys:

public class PluginsModule extends AbstractModule {
  @ProvidesIntoMap
  @StringMapKey("user")
  Plugin provideUserPlugin(UserService userService) {
    return new UserPlugin(userService);
  }
  
  @ProvidesIntoMap
  @StringMapKey("order")
  Plugin provideOrderPlugin() {
    return new OrderPlugin();
  }
  
  @ProvidesIntoMap
  @StringMapKey("notification")
  @Named("web")  // Can combine with binding annotations
  Plugin provideWebNotificationPlugin() {
    return new WebNotificationPlugin();
  }
}

Provider Method Binding with Class Keys:

public class ProcessorsModule extends AbstractModule {
  @ProvidesIntoMap
  @ClassMapKey(String.class)
  Processor provideStringProcessor() {
    return new StringProcessor();
  }
  
  @ProvidesIntoMap
  @ClassMapKey(Integer.class)
  Processor provideIntegerProcessor() {
    return new IntegerProcessor();
  }
}

// Injection
@Inject
public ProcessorRegistry(Map<Class<?>, Processor> processors) {
  this.processors = processors; // {String.class -> StringProcessor, Integer.class -> IntegerProcessor}
}

Custom Map Key Annotation:

// Define custom enum key annotation
public enum Environment { DEV, TEST, PROD }

@MapKey(unwrapValue = true)
@Retention(RUNTIME)
public @interface EnvironmentKey {
  Environment value();
}

// Use in provider method
@ProvidesIntoMap
@EnvironmentKey(Environment.PROD)
DatabaseConfig provideProdConfig() {
  return new ProductionDatabaseConfig();
}

Multiple Modules Contributing:

// CandyModule
public class CandyModule extends AbstractModule {
  @Override
  protected void configure() {
    MapBinder<String, Snack> mapbinder = 
        MapBinder.newMapBinder(binder(), String.class, Snack.class);
    mapbinder.addBinding("chocolate").to(Chocolate.class);
    mapbinder.addBinding("gummies").to(Gummies.class);
  }
}

// ChipsModule
public class ChipsModule extends AbstractModule {
  @Override
  protected void configure() {
    MapBinder<String, Snack> mapbinder = 
        MapBinder.newMapBinder(binder(), String.class, Snack.class);
    mapbinder.addBinding("doritos").to(Doritos.class);
    mapbinder.addBinding("cheetos").to(Cheetos.class);
  }
}

// Final injected Map contains entries from both modules

Duplicate Key Handling:

public class ConfigModule extends AbstractModule {
  @Override
  protected void configure() {
    MapBinder<String, String> mapbinder = MapBinder.newMapBinder(binder(), String.class, String.class)
        .permitDuplicates(); // Enable duplicate key support
    mapbinder.addBinding("env").toInstance("dev");
    mapbinder.addBinding("env").toInstance("test"); // Would normally fail without permitDuplicates()
  }
}

// With permitDuplicates(), additional Map types are bound:
@Inject Map<String, Set<String>> multiValueMap; // {"env" -> {"dev", "test"}}
@Inject Map<String, Set<Provider<String>>> multiProviderMap;

Key Features

  • Key Uniqueness: By default, keys must be distinct or injection will fail
  • Iteration Order: Map iteration order is consistent with binding order within a module
  • Multi-Module Support: Multiple modules can contribute to the same Map
  • Provider Support: Can inject Map<K, Provider<V>> for lazy value evaluation
  • Scoping: Individual values can be scoped independently
  • Annotation Support: Different Maps of same key/value type can be created using binding annotations
  • Duplicate Key Support: When enabled, provides additional Map<K, Set<V>> bindings
  • Custom Key Types: Extensible key annotation system supports any serializable type

Install with Tessl CLI

npx tessl i tessl/maven-com-google-inject-extensions--guice-multibindings

docs

index.md

map-binding.md

optional-binding.md

set-binding.md

tile.json