Qualifiers & collections
Two related needs: telling apart several beans of the same type, and injecting all providers of a type at once.
Key<T>: type + optional qualifier
Every bean is identified by a Key<T>, which is a type plus an optional qualifier:
import be.theking90000.scope.Key;
Key<Message> hello = Key.of(Message.class, "hello");
Key<Message> bye = Key.of(Message.class, "bye");
scope.seed(hello, new Message("Hello"));
scope.seed(bye, new Message("Bye"));Key.of(type) builds an unqualified key; Key.of(type, qualifier) a qualified one. The qualifier is any object (commonly a String). Every seed / provide / bind / get / provider / providers method has a Key overload.
@Named: qualify an injected parameter
To inject a qualified bean by constructor, annotate the parameter:
import be.theking90000.scope.Named;
record Greeter(@Named("hello") Message message) {}
Greeter greeter = scope.get(Greeter.class); // gets the "hello" Messagebind accepts qualified keys too:
Key<Service> serviceKey = Key.of(Service.class, "game");
scope.bind(serviceKey);
Service service = scope.get(serviceKey);Reach for a qualifier whenever there is more than one conceptual value of the same type (e.g. two DataSources, several Messages).
Injecting all providers of a type
Sometimes you want every provider of a type, not a single one. Register several:
record Counter(int value) {}
scope.seed(Counter.class, new Counter(1));
scope.provide(Counter.class, () -> new Counter(2));Asking for a single Counter now fails with AmbiguousBeanException. Instead, request the collection:
for (Provider<Counter> counter : scope.providers(Counter.class).get()) {
System.out.println(counter.get().value());
}providers(...) returns a MultiProvider<Counter>, which is itself a Provider<Iterable<Provider<Counter>>>.
Collection injection by constructor
A class can declare that it wants all providers of a type:
record Counters(Provider<Iterable<Provider<Counter>>> counters) {
int sum() {
int result = 0;
for (Provider<Counter> counter : counters.get()) {
result += counter.get().value();
}
return result;
}
}The exact type matters — read it inside out:
Provider<Iterable<Provider<Counter>>>
// └ outer Provider : the collection itself is lazy
// └ Iterable : several providers are requested
// └ Provider<Counter> : each element creates or returns a CounterThis is the canonical way to model plugin-style "register many, consume all" patterns (commands, listeners, validators…). See Recipes.
By default the collection is gathered with the
NEARESTstrategy. To gather contributions from every visible scope (not just the nearest one per branch), use theDEEPstrategy — see Multi-parent scopes.
Next: Scopes & lifecycle.