Inject Collection of Beans in Spring

An example oriented tutorial on auto wiring and injecting references of multiple beans of a same type as a list, set or a map in Spring Framework.

Overview

Spring auto wiring is a powerful framework for injecting dependencies. It provides a flexible and dynamic way of declaring and auto wiring dependencies by different ways. Sometimes, we may want to find all implementations of a super class or super interface and perform a common action on them. To do that, we need to hold references to such Spring Beans in some sort of collection. Spring framework provides a painless and more readable way to collect all the beans that belong to a same type.

In this tutorial we will learn to Inject references of multiple beans into a collection and to sort the bean references when they are injected.

Setup Multiple Beans of Same Type

Before we begin with Injecting Bean References as List, Set, or Map we will first create an abstract class and a couple sub classes. Remember, instead of an abstract class we can also create an interface and a multiple implementations and spring will work in the same way.

Next is a super class which will also serve as a Type in our example.

public abstract class StorageService {
}Code language: Java (java)

Then, first subclass,

@Component
public class FileSystemStorageService extends StorageService{
}Code language: Java (java)

second subclass,

@Component
public class DatabaseStorageService extends StorageService{
}Code language: Java (java)

and, third subclass.

@Component
public class CloudStorageService extends StorageService {
}Code language: Java (java)

In order for Spring to be able to instantiate and manage various classes, we need to make them Spring Beans. Thus, we have marked each of our subclasses with @Component annotation.

Inject Bean References as List

We will use the three beans to show how to inject Beans of same type as a List. To do that we need to define an instance variable, which is a list of super class or super interface type.

private final List<StorageService> storeServices;Code language: Java (java)

The smart auto wiring capabilities in Spring, helps it to understand the intended type. Thus, spring will scan all the beans that belong to the given type, or in other words beans that extend StorageService and prepares a list.

Let’s see a complete example of auto wiring beans as a list.

@Component
public class FileProcessor {
    private final List<StorageService> storeServices;

    @Autowired
    public FileProcessor(List<StorageService> storeServices) {
        this.storeServices = storeServices;
    }

    @PostConstruct
    public void listImplementations() {
        storeServices
                .forEach(storageService -> System.out.println(storageService.getClass().getSimpleName()));
    }
}Code language: Java (java)

This class has an instance variable, which is a List of StorageService type. Next, we are using Constructor dependency injection by using @Autowired annotation on the constructor. After that, we are printing the simple names of each of the elements of the list.

Remember, spring can inject collection of beans of a same type by using any type of injection method we use. Thus, we can also use Field dependency injection, or a Setter dependency Injection.

When we start the application, we see the list is correctly populated with references to all three implementations of the superclass StorageService.

CloudStorageService
DatabaseStorageService
FileSystemStorageService

Inject Bean References as Set

Similarly, we can inject a Set of beans that belong to a same type.

private final Set<StorageService> storeServices;

@Autowired
public FileProcessor(Set<StorageService> storeServices) {
    this.storeServices = storeServices;
}Code language: Java (java)

Inject Bean References as Map

Interestingly we can also inject beans of same type as a Map. All we need to do is define an instance variable of type Map.

private final Map<String, StorageService> storeServices;Code language: Java (java)

Spring has an excellent type inference system that helps it to recognise intended type of the dependency being injected. When Spring sees the auto wired map having StorageService as the value it prepares a map where key is the bean id. The Bean Id can be specified on each bean. However, if it is not specified Spring will derive it from the name of the bean.

Next is a complete example of injecting Bean references as a Map.

@Component
public class FileProcessor {
    private final Map<String, StorageService> storeServices;

    @Autowired
    public FileProcessor(Map<String, StorageService> storeServices) {
        this.storeServices = storeServices;
    }

    @PostConstruct
    public void listImplementations() {
        storeServices
                .forEach((id, storageService) ->
                        System.out.println(id + ": " + storageService.getClass().getSimpleName()));
    }
}Code language: Java (java)

Note that in the post construct method we are printing each entry from the map on console. The output will look like this:

cloudStorageService: CloudStorageService
databaseStorageService: DatabaseStorageService
fileSystemStorageService: FileSystemStorageService

Spring has intelligently populated the Map of bean Id and the bean references correctly.

Sort and Inject Beans References as List

We can also provide a custom sort order for the auto wired List of Bean References. To do that, we can use @Order annotation on the beans to specify their index position.

For example, next snippet shows all three beans with specified sort orders.

@Component
@Order(2)
public class CloudStorageService extends StorageService {
}


// .....

@Order(2)
@Component
public class DatabaseStorageService extends StorageService {
}


// .....

@Order(1)
@Service("storeService")
public class FileSystemStorageService extends StorageService {
}Code language: Java (java)

When we run the application, we see the output is now in the given order.

FileSystemStorageService
CloudStorageService
DatabaseStorageService

Summary

In this tutorial, we understood that Spring Framework helps us collect multiple beans of a same type and inject them into another bean in the form of collections. We created an abstract class and three subclasses, of which all three sub classes were Spring Beans.

Then we created example of injecting multiple beans into a List, into a Set, and lastly into a Map. Also, we learned that when we auto wire multiple beans of same type as a Map, the key is the Id of the bean. Finally, we learned that we can specify the order for beans while injecting them into Collections.

For more on Spring and Spring Boot, please visit Spring Tutorials.