Mastering Spring @Autowired Collection Injection: Lists, Sets, Maps Explained
This article explains how Spring's @Autowired annotation can inject collections such as List, Set, and Map, detailing injection mechanisms, ordering, practical examples like filter chains and validators, advanced features like @Qualifier and custom ordering, and important considerations such as circular dependencies and performance impacts.
Interview Answer
Spring's @Autowired annotation can be applied to various collection types such as List, Set, and Map. This powerful feature allows Spring to automatically collect and inject multiple beans of the same type.
This mechanism is ideal for implementing plugin architectures, filter chains, multi‑strategy implementations, and other scenarios where loose coupling and extensibility are required. For example, you can define an interface, provide multiple implementations, and let Spring automatically gather and inject them without manual registration.
Spring supports the following common collection injection methods: List<BeanType> – inject all matching beans, allowing duplicates. Set<BeanType> – inject all matching beans, without duplicates. Map<String, BeanType> – inject beans with their names as keys. Map<Class<?>, BeanType> – inject beans with their class types as keys.
Detailed Analysis
1. List Injection
When injecting a List, Spring automatically collects all matching beans and orders them according to the @Order annotation or the Ordered interface.
package com.qy.filter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.List;
// Filter interface
public interface RequestFilter {
String filter(String request);
}
// Security filter
@Component
@Order(1) // highest priority, executed first
class SecurityFilter implements RequestFilter {
@Override
public String filter(String request) {
System.out.println("Executing security filter");
return request + " [secured]";
}
}
// Logging filter
@Component
@Order(2) // second
class LoggingFilter implements RequestFilter {
@Override
public String filter(String request) {
System.out.println("Executing logging");
return request + " [logged]";
}
}
// Caching filter
@Component
@Order(3) // last
class CachingFilter implements RequestFilter {
@Override
public String filter(String request) {
System.out.println("Executing caching");
return request + " [cached]";
}
}
// Filter chain service
@Service
public class FilterChainService {
private final List<RequestFilter> filters;
@Autowired
public FilterChainService(List<RequestFilter> filters) {
this.filters = filters;
System.out.println("Injected filter count: " + filters.size());
}
public String processRequest(String request) {
String result = request;
for (RequestFilter filter : filters) {
result = filter.filter(result);
}
return result;
}
}In this example, Spring injects all RequestFilter implementations into the list according to the order defined by @Order, allowing the filters to execute in the predetermined sequence.
2. Set Injection
Set injection works similarly to List but guarantees that duplicate beans are not included.
package com.qy.validator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.Set;
// Validator interface
public interface Validator {
boolean validate(String data);
String getValidatorName();
}
// Non‑empty validator
@Component
class NotEmptyValidator implements Validator {
@Override
public boolean validate(String data) {
boolean valid = data != null && !data.trim().isEmpty();
System.out.println("Non‑empty validation: " + (valid ? "passed" : "failed"));
return valid;
}
@Override
public String getValidatorName() {
return "Non‑empty Validator";
}
}
// Length validator
@Component
class LengthValidator implements Validator {
@Override
public boolean validate(String data) {
boolean valid = data != null && data.length() <= 100;
System.out.println("Length validation: " + (valid ? "passed" : "failed"));
return valid;
}
@Override
public String getValidatorName() {
return "Length Validator";
}
}
// Validation service
@Service
public class ValidationService {
private final Set<Validator> validators;
@Autowired
public ValidationService(Set<Validator> validators) {
this.validators = validators;
System.out.println("Injected validators:");
validators.forEach(v -> System.out.println("- " + v.getValidatorName()));
}
public boolean validate(String data) {
return validators.stream().allMatch(v -> v.validate(data));
}
}3. Map<String, BeanType> Injection
When using a Map, the bean name can be used as the key and the bean instance as the value.
package com.qy.processor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.Map;
// Processor interface
public interface DataProcessor {
String process(String data);
}
// JSON processor
@Component("json")
class JsonProcessor implements DataProcessor {
@Override
public String process(String data) {
System.out.println("Processing JSON data");
return "{\"processed\": \"" + data + "\"}";
}
}
// XML processor
@Component("xml")
class XmlProcessor implements DataProcessor {
@Override
public String process(String data) {
System.out.println("Processing XML data");
return "<processed>" + data + "</processed>";
}
}
// Processor service
@Service
public class ProcessorService {
private final Map<String, DataProcessor> processorMap;
@Autowired
public ProcessorService(Map<String, DataProcessor> processorMap) {
this.processorMap = processorMap;
System.out.println("Injected processors: " + processorMap.keySet());
}
public String processData(String type, String data) {
DataProcessor processor = processorMap.get(type);
if (processor != null) {
return processor.process(data);
} else {
throw new IllegalArgumentException("Unsupported data type: " + type);
}
}
}How Collection Injection Works
When Spring encounters a collection‑type @Autowired injection point, it follows these steps:
Identify collection type : Spring detects that the field or parameter is a List, Set, or Map.
Extract element type : It determines the generic element type.
Collect matching beans : All beans compatible with the element type are gathered.
Order (if needed) : For Lists, beans are ordered according to @Order or the Ordered interface.
Inject collection : The collected beans are injected into the target collection.
Below is a simplified flow diagram of Spring’s collection injection process.
Application Scenarios for Collection Injection
Plugin systems : Automatic discovery and registration of plugins without manual configuration.
Filter/Interceptor chains : Building ordered processing pipelines.
Multi‑strategy implementations : Selecting different processing strategies based on conditions.
Command handlers : Routing commands to appropriate handlers automatically.
Validator collections : Combining multiple validation rules.
Event listeners : Automatically collecting and registering event listeners.
Advanced Features and Considerations
1. Use @Qualifier to Precisely Control Injection
When multiple beans of the same type exist, @Qualifier can specify which beans to inject.
@Autowired
@Qualifier("highPriority")
private List<TaskProcessor> highPriorityProcessors;2. Custom Ordering
Beyond @Order, you can implement the Ordered or PriorityOrdered interfaces to control bean order.
@Component
public class CriticalFilter implements RequestFilter, PriorityOrdered {
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE; // highest priority
}
@Override
public String filter(String request) {
// implementation code
}
}3. Use Generic Types as Qualifiers
Spring can use generic type information as a qualifier for injection.
interface Repository<T> { /* ... */ }
@Component
class UserRepository implements Repository<User> { /* ... */ }
@Component
class ProductRepository implements Repository<Product> { /* ... */ }
@Service
public class Service {
@Autowired
private List<Repository<User>> userRepositories; // only UserRepository injected
}4. Precautions
Circular dependencies : Collection injection can introduce circular dependency issues and should be handled carefully.
Performance impact : Injecting large numbers of beans may affect application startup time.
Order stability : Collections without explicit ordering do not guarantee stable order.
Empty collection handling : When no matching beans are found, Spring injects an empty collection rather than null.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Xuanwu Backend Tech Stack
Primarily covers fundamental Java concepts, mainstream frameworks, deep dives into underlying principles, and JVM internals.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
