Spring — Creating beans of same type using Spring Annotations

Sumit Kumar
5 min readOct 3, 2023

--

1. Overview

Spring beans are objects that serve as the foundation of your application and are created and handled by the Spring IoC container. Instantiating a spring bean is just like creating objects of simple Java classes. However, creating multiple beans of the same class could be a tricky thing in spring. In this tutorial, we’ll see how we can create multiple beans of the same class using annotations in Spring framework.

2. Approach 1 — using Java Config class

This is the simplest and easiest way to create multiple beans of the same class using annotations. In this approach, we’re going to use Java based configuration class to configure multiple beans of the same class. Let’s consider a simple example. We’ve a Person class that has two class members namely, firstName and lastName as mentioned below.

public class Person {

private String firstName;

private String lastName;

public Person(String firstName, String secondName) {

super();

this.firstName = firstName;

this.lastName = secondName;

}

@Override

public String toString() {

return “Person [firstName=” + firstName + “, secondName=” + lastName + “]”;

}

}

Next, we’re going to create a configuration class PersonConfig and instantiate multiple beans of Person class. Have a look at the code given below.

@Configuration

public class PersonConfig {

@Bean

public Person personOne() {

return new Person(“Harold”, “Finch”);

}

@Bean

public Person personTwo() {

return new Person(“John”, “Reese”);

}

}

Here, @Bean will instantiate two beans with method name as the bean id and register it within the BeanFactory (Spring container). You can read more about @Bean annotation from the article given here.

Next, we can initialize the spring container and request for any of the bean from the spring container. The limitation with this approach is that here you’ve to manually instantiate beans using the new keyword. So, if the number of beans of same class increases, you need to register it first and creates its bean in the config class. This makes it more Java specific rather than Spring specific approach.

3. Approach 2 — using Inheritance

In this approach, we’re going to use the concept of inheritance and Java config class for instantiating beans, just like what we did in the previous approach. First, we’re going to create multiple subclasses that extends the Person super class.

public class PersonOne extends Person{

public PersonOne(String firstName, String lastName) {

super(firstName, lastName);

}

}

public class PersonTwo extends Person {

public PersonTwo(String firstName, String lastName) {

super(firstName, lastName);

}

}

Next, in the PersonConfig file, we’re going to use @Qualifier annotation to configure different beans of type Person as follows.

@Configuration

public class PersonConfig {

@Bean

@Qualifier(“personOne”)

public Person personOne() {

return new Person(“Harold”, “Finch”);

}

@Bean

@Qualifier(“personTwo”)

public Person personTwo() {

return new Person(“John”, “Reese”);

}

}

The use of @Qualifier in this approach will remove the ambiguity of the bean that we want to use when the Spring container finds multiple beans of the same type. You can read more about @Qualifier annotation from the article given here.

The limitation of this approach is similar to the previous one, since here also we’re instantiating beans in a Java specific way. Furthermore, the usage of inheritance will increase the overall complexity of the code.

4. Approach 3 — using BeanFactoryPostProcessor

The third and final approach make use of the custom implementation of BeanFactoryPostProcessor interface for creating multiple bean instances of the same class. To understand this approach in a better way, we’re extending the same example further. Suppose there’s a class Human which is dependent upon multiple instances of Person class. Have a look at the class given below.

public class Human implements InitializingBean{

private Person personOne;

private Person personTwo;

@Override

public void afterPropertiesSet() throws Exception {

Assert.notNull(personOne, “Harold is alive!”);

Assert.notNull(personTwo, “John is alive!”);

}

/* Setter injection */

public void setPersonOne(Person personOne) {

this.personOne = personOne;

this.personOne.setFirstName(“Harold”);

this.personOne.setSecondName(“Finch”);

}

public void setPersonTwo(Person personTwo) {

this.personTwo = personTwo;

this.personTwo.setFirstName(“John”);

this.personTwo.setSecondName(“Reese”);

}

}

The InitializingBean interface invokes the afterPropertiesSet() method to check whether BeanFactory has set all the bean properties and satisfied other dependencies. Further, using setter injection, we’re injecting and initializing two Person class beans namely personOne and personTwo.

Next, we are going to create Person class that implements the FactoryBean interface. The FactoryBean interface is used when we need to produce objects by themselves that can be managed by the Spring framework. This interface is intended to create more instances if the bean that implements it. In our case, it generates instances of the type Person class. You can read more about the BeanFactory class from the article given over here.

@Qualifier(value = “personOne, personTwo”)

public class Person implements FactoryBean {

private String firstName;

private String secondName;

public Person() {

// initialization code (optional)

}

@Override

public Class<Person> getObjectType() {

return Person.class;

}

@Override

public Object getObject() throws Exception {

return new Person();

}

public boolean isSingleton() {

return true;

}

public String getFirstName() {

return firstName;

}

public void setFirstName(String firstName) {

this.firstName = firstName;

}

public String getSecondName() {

return secondName;

}

public void setSecondName(String secondName) {

this.secondName = secondName;

}

@Override

public String toString() {

return “Person [firstName=” + firstName + “, secondName=” + secondName + “]”;

}

}

The second important thing to notice here is the use of @Qualifier annotation that contains names or bean ids of multiple Person types at class level. Now, there is a reason behind using @Qualifier at class level.

Next, we’re going to use the custom implementation of BeanFactoryPostProcessor interface which intends to scan all the class annotated with @Qualifier and extract names (bean ids) from that annotation and manually create instances of that class type with the specified names.

public class PersonFactoryPostProcessor implements BeanFactoryPostProcessor {

@Override

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

// TODO Auto-generated method stub

Map<String, Object> map = beanFactory.getBeansWithAnnotation(Qualifier.class);

for(Map.Entry<String,Object> entry : map.entrySet()){

createInstances(beanFactory, entry.getKey(), entry.getValue());

}

}

private void createInstances(

ConfigurableListableBeanFactory beanFactory,

String beanName,

Object bean

) {

Qualifier qualifier = bean.getClass().getAnnotation(Qualifier.class);

for(String name : extractNames(qualifier)){

Object newBean = beanFactory.getBean(beanName);

beanFactory.registerSingleton(name.trim(), newBean);

}

}

private String[] extractNames(Qualifier qualifier){

return qualifier.value().split(“,”);

}

}

Here, the custom BeanFactoryPostProcessor implementation will get invoked once the spring container is initialized.

To keep things simple, here we’re using XML configuration to initialize the custom implementations and to perform setter injection for the Human class.

<?xml version=”1.0" encoding=”UTF-8"?>

<beans xmlns=”http://www.springframework.org/schema/beans"

xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance"

xmlns:context=”http://www.springframework.org/schema/context"

xsi:schemaLocation=”http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd">

<! — Define your beans here →

<bean class=”com.baeldung.multiple_bean_instantiation.solution3.PersonFactoryPostProcessor”>

</bean>

<bean class=”com.baeldung.multiple_bean_instantiation.solution3.Person”></bean>

<bean name=”human” class=”com.baeldung.multiple_bean_instantiation.solution3.Human”>

<property name=”PersonOne” ref=”personOne”/>

<property name=”PersonTwo” ref=”personTwo”/>

</bean>

</beans>

The limitation of this approach lies in its complexity as it is not encouraged to use since it’s not the natural way of configuring beans in a typical Spring application. However, this approach is more Spring specific and serves the purpose of instantiating multiple beans of similar type using annotations.

5. Conclusion

In this article, we’ve learned about instantiating multiple beans of the same class using Spring annotations using three different approaches. The first two approaches are simple and Java specific while the third one is a bit tricky and complex but serves the purpose of bean creation using annotations.

The code for the given example is available over on Github.

--

--

Sumit Kumar
Sumit Kumar

Written by Sumit Kumar

Assistant Professor of Computer Science at The NorthCap University, Gurugram, India.

No responses yet