Injecting a List<T> or Map<String, T> of all beans of a t… — Cracked Java
// Spring Framework & Spring Boot · @Autowired, @Qualifier, @Primary, Injection Resolution
MidCoding

Injecting a List<T> or Map<String, T> of all beans of a type.

When you inject List<T> or Map<String, T>, Spring gives you every bean of type T — this is the intended way to collect all implementations of an interface, not an ambiguity error. It's the basis of every plugin/strategy registry pattern in Spring.

List<T> — all beans, ordered

public interface Validator { void validate(Order o); }

@Component class AmountValidator  implements Validator { ... }
@Component class AddressValidator implements Validator { ... }

@Service
class OrderService {
    private final List<Validator> validators;
    OrderService(List<Validator> validators) { this.validators = validators; }

    void check(Order o) { validators.forEach(v -> v.validate(o)); }
}

Spring injects all matching beans. Note this is not NoUniqueBeanDefinitionException territory — asking for a collection is unambiguous by definition.

Controlling order

Collection order is not guaranteed by registration; control it with @Order or by implementing Ordered:

@Component @Order(1) class AmountValidator  implements Validator { ... }
@Component @Order(2) class AddressValidator implements Validator { ... }

Lower values come first. Without @Order, don't rely on the order.

Map<String, T> — keyed by bean name

@Service
class PaymentRouter {
    private final Map<String, PaymentGateway> gateways;
    PaymentRouter(Map<String, PaymentGateway> gateways) { this.gateways = gateways; }

    void pay(String name, Order o) { gateways.get(name).charge(o); }
}

The String keys are the bean names (stripe, paypal, …). This is perfect for "dispatch by a config value" routing without a switch statement. The key type must be String — that's the only supported map key.

Edge cases worth naming

  • Empty collection: if no beans of the type exist, you get an empty List/Map, not null and not an error. (Pre-4.3 behavior differed; modern Spring injects empty.)
  • @Primary is ignored for collection injection — you're collecting all, so there's no "default" to pick.
  • ObjectProvider<T> also exposes .stream() / .orderedStream() for the same effect lazily, which is handy when you want the beans resolved on demand.

Mark your status