Spring Foundations Part 1: Adding Beans to Context

Jawad Zaarour
6 min readMay 5, 2024

--

Welcome to the beginning of the “Spring Foundations” series. In this first part, we’ll cover key concepts and different ways to add beans to the context. Let’s start our exploration into the basics of Spring framework !

Different methods are available for adding new object instances (i.e., beans) to the Spring context, allowing Spring to manage and integrate features into application.

You can add beans in the context in the following ways:

  • Using the @Bean annotation
  • Using stereotype annotations
  • Programmatically

We start with a simple class, Car with make and year attributes.

@Getter
@Setter
public class Car {

private String make;
private int year;

}

After setting up the project and creating an instance of the Spring context, the objective is to add the Car object to the context using different approaches.

1. @Bean

Using the @Bean annotation involves adding object instances to the Spring context. This method is straightforward, especially for beginners. Here’s a basic outline of the process:

  1. Create a configuration class annotated with @Configuration to configure the Spring context.
  2. Define a method within the configuration class that returns the object instance to be added, annotating it with @Bean.
  3. Instruct Spring to utilize the configuration class.

To verify the Car instance is indeed part of the context now, you can refer to the instance and print its year field in the console, as presented in the following listing.

public class Main {
public static void main(String[] args) {
var context = new AnnotationConfigApplicationContext(ProjectConfig.class);
Car car = context.getBean(Car.class);
System.out.println(car.getYear());
}
}

Also, you can add more beans of different types into the Spring context. Let’s also add a String and an Integer.

You can now refer to these two new beans in the same way we did with the car. The next listing shows you how to change the main method to print the new beans’ values.

public class Main {
public static void main(String[] args) {
var context = new AnnotationConfigApplicationContext(ProjectConfig.class);

// Retrieving Car bean
Car car = context.getBean(Car.class);
System.out.println("Car Year: " + car.getYear());

// Retrieving String bean
String s = context.getBean(String.class);
System.out.println("String Bean Value: " + s);

// Retrieving Integer bean
Integer n = context.getBean(Integer.class);
System.out.println("Integer Bean Value: " + n);
}
}

Note that the Spring context’s purpose is to manage instances needed by Spring app. While not every object needs to be added to it, understanding how to add beans is essential.

You can declare as many instances of the same type as you wish by simply declaring more methods annotated with the @Bean annotation. The following listing shows you how I’ve declared three beans of type Car in the configuration class.

In this configuration class:

  • Three beans of type Car are defined: car1, car2, and car3.
  • Each method annotated with @Bean returns a Car instance with different make, model, and year.

When retrieving beans from the context, specifying just the type will result in an exception. To resolve this, you need to specify the name of the bean you want to retrieve explicitly. For example:

Car car = context.getBean("car2", Car.class);

If you want to give a custom name to the bean, you can use the name or value attributes of the @Bean annotation:

@Bean(name = "CAR_NUMBER_1")
Car car2() {
var car = new Car();
car.setMake("car1");
car.setYear(2023);
return car;
}

Primary Bean

When you have multiple beans of the same type in the Spring context, you can make one of them primary. This is achieved by annotating the desired bean method with @Primary. The primary bean is Spring's default choice when selecting from multiple options of the same type, and it is used when you don't specify a bean name explicitly.

Here’s an example of defining a primary bean:

@Bean
@Primary
Car car2() {
var car = new Car();
car.setMake("car2");
car.setYear(2023);
return car;
}

If you refer to a Car without specifying the name, Spring will now select car2 by default.

2. Stereotype annotations

In this section, we explore an alternative approach for adding beans to the Spring context using stereotype annotations. Unlike the @Bean annotation, stereotype annotations like @Component streamline the process, reducing the amount of code needed to instruct Spring.

Here’s a breakdown of the steps involved:

  1. Mark the Class as a Component: Use the desired stereotype annotation (e.g., @Component) to mark the class for which you want Spring to manage an instance.
  2. Configure Component Scanning: To enable Spring to find and manage classes marked with stereotype annotations, use the @ComponentScan annotation in the configuration class. Specify the base package(s) where Spring should look for these annotated classes.

For example, let’s consider the Car class:

@Getter
@Setter
@Component
public class Car {

private String make;
private int year;

}

By default, Spring doesn’t search for classes annotated with stereotype annotations. So, to instruct Spring to do so, we need to configure component scanning in the configuration class:

@Configuration
@ComponentScan(basePackages = "where.classes.annotated.with.Component")
public class ProjectConfig {

}

Here’s a comparison between using the @Bean annotation and stereotype annotations to add beans to the Spring context:

When working on real projects, you’ll find that using stereotype annotations like @Component or @Service is generallypreferred due to the reduced coding effort involved. The @Bean annotation, while still valuable, is best reserved for situations where adding a stereotype annotation directly to the class isn’t possible. This might happen when you’re dealing with classes from external libraries that you cannot modify.

3. Adding beans Programmatically

In this section, we explore programmatically adding beans to the Spring context (Spring 5 and later), offering flexibility beyond what @Bean or stereotype annotations provide. This approach is useful when you need custom logic for adding beans based on specific application configurations.

Using registerBean() Method: To add a bean programmatically, call the registerBean() method of the ApplicationContext instance. This method takes four parameters:


<T> void registerBean(
String beanName, //Defines a name for the bean.

Class<T> beanClass, //Specifies the class that defines the bean.

Supplier<T> supplier, //An instance that returns the bean instance.

BeanDefinitionCustomizer... customizers //to configure different characteristics of the bean.
);
var context = new AnnotationConfigApplicationContext(ProjectConfig.class);

Car car = new Car();
car.setMake("car1");
car.setYear(2023);

Supplier<Car> carSupplier = () -> car;

context.registerBean("car", Car.class, carSupplier);

Car c = context.getBean(Car.class);
System.out.println(c.getModel());

Also, you can use BeanDefinitionCustomizer instances as the last parameters of registerBean() to set different characteristics of the beans. For example, you can make a bean primary by configuring it as shown below:

context.registerBean("car", Car.class, carSupplier , bd -> bd.setPrimary(true));

This approach is particularly useful when you need dynamic bean registration based on runtime conditions or when integrating with third-party libraries where you cannot directly use stereotype annotations.

--

--

Jawad Zaarour

Software Engineer | Java | Spring Boot | Microservices | Ops | Physics