What is Spring Context? 

The Spring Context, or Application Context, serves as a storage area in your application’s memory where Spring manages the objects you need, called beans. A bean is simply an object that Spring creates and controls on your behalf. By allowing Spring to manage your beans, you benefit from useful features like dependency injection, loose coupling, and aspect-oriented programming (AOP). These features make your application more flexible, maintainable, and modular.

Practical Applications of Spring Context

In an e-commerce application, you may need a database connection to manage product information and process orders. Without Spring, you would have to create and manage a database connection object in every service class that interacts with the database. For instance, you might have a ProductService class responsible for retrieving product details from the Products table and a separate OrderService class that handles order placements and management within the Orders table. If both classes independently manage their own database connections, it leads to repetitive code and a cluttered codebase.

By using Spring, you can define the database connection as a bean in the Spring Context. Once defined, Spring will inject this connection into both the ProductService and OrderService classes. This ensures that both services can utilize the same database connection without duplicating connection logic. As a result, your business code remains clean and focused on the core functionalities of product management and order processing, while Spring efficiently manages the complexities of the database connection behind the scenes.

Adding Beans to the Spring Context

However, Spring doesn’t automatically recognize the beans you define in your application. You must explicitly add them to the Spring Context for Spring to manage them. The Spring Context is like a container, or bucket, where you place the objects that Spring will control. The key is that Spring can only manage the objects you add to the context, so it’s crucial to know how to do this effectively.

In this post, assuming you have a basic understanding of how to set up a Maven project, we’ll explore the three main ways to add beans to the Spring Context. We can use any objects but for the sake of simplicity, let’s add a Book class bean to the spring context. Here is a Book class with two attributes: title and author

public class Book {
    private String title;
    private String author;

    // Constructor omitted

    @Override
    public String toString() {
        return title + " by " + author;
    }
}
  1. Using the Bean annotation
@Configuration
public class AppConfig {

    @Bean
    public Book book() {
        return new Book("The Great Gatsby", "F. Scott Fitzgerald");
    }

    @Bean
    public Float pi() {
        return 3.14f; // The mathematical constant Pi
    }
}

The @Bean annotation is a flexible way to add beans to the Spring Context. When used in a class annotated with @Configuration, it instructs Spring to invoke the method during context initialization and register the returned value. Methods annotated with @Bean in @Configuration classes return objects that the Spring context manages.

Using @Configuration typically requires @ComponentScan, unless you are using Spring Boot, which automatically scans for components. By default, the name of the bean is the method name converted to lowercase; for instance, the name of the bean from the book() method is “book.”

This approach is particularly useful for defining beans for classes where you cannot modify the original code. For example, to create a bean for the mathematical constant Pi (3.14), you cannot annotate a float variable directly. Instead, define a method in your @Configuration class that returns Pi as a bean, allowing you to manage it within the Spring context and easily access it in your application.

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(Main.class, args);
        Book book = context.getBean(Book.class);
        Float pi = context.getBean(Float.class);
        System.out.println(book);
        System.out.println(pi);
    }
}

This will log: “The Great Gatsby by F. Scott Fitzgerald” and “3.14”.

  1. Using stereotype annotations such as Component
@Component
public class Book {
    private String title;
    private String author;

    // Default constructor
    public Book() {
        this.title = "The Great Gatsby";
        this.author = "F. Scott Fitzgerald";
    }

    @Override
    public String toString() {
        return title + " by " + author;
    }
}

With the @SpringBootApplication annotation, Spring Boot automatically scans all packages for stereotype annotations like @Component and creates beans using the class’s constructor. However, this process doesn’t allow for passing in parameters, making it impossible to customize the bean’s properties directly. Besides @Component, there are other common stereotype annotations which play specific roles in a Spring application. 

In the case of the Book class, we had to modify the constructor to use hardcoded values for the title and author attributes for the sake of this example. Since @Component doesn’t support customization during instantiation, we cannot specify these attributes directly. Instead, we provide default values in the constructor, which means that any instance of Book created by Spring will have these hardcoded values. If you need different configurations or values for your beans, it’s better to use the @Bean annotation in a configuration class, as it allows you to define multiple beans with varying properties, giving you the flexibility you need.

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(Main.class, args);
        Book book = context.getBean(Book.class);
        System.out.println(book);
    }
}

This will log out: “The Great Gatsby by F. Scott Fitzgerald”.

  1. Adding beans programmatically
@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        GenericApplicationContext context = (GenericApplicationContext) SpringApplication.run(Main.class, args);

        // Check if command-line arguments are provided
        if (args.length >= 2) {
            String title = args[0];
            String author = args[1];

            // Register a Book bean with the provided title and author
            context.registerBean(Book.class, () -> new Book(title, author));
        } else {
            // Default values if no arguments are provided
            context.registerBean(Book.class, () -> new Book("The Great Gatsby", "F. Scott Fitzgerald"));
        }

        // Retrieve the registered bean
        Book book = context.getBean(Book.class);
        System.out.println(book);
    }
}

GenericApplicationContext is a specific type of Spring application context that makes it easy to create and manage beans. When you call SpringApplication.run(), it usually returns a general ApplicationContext, which lacks some methods for dynamic bean registration. By casting it to GenericApplicationContext, you unlock additional methods like registerBean(), allowing you to create beans based on user inputs, such as command-line arguments. This gives you more flexibility and control over how beans are registered in your Spring application.

The registerBean() method allows you to add beans to the Spring context programmatically, making it easy to customize bean properties at runtime. Unlike @Bean and @Component, which are used for defining beans in a static way, registerBean() lets you create beans based on user inputs, like command-line arguments. This means you can change the properties of a bean when you start your application, which is useful if you want to configure things differently without changing your code. This approach is especially useful for applications that need to adapt based on user preferences or external conditions.

In summary, the Spring Context is essential for managing beans in your application. You can define beans using @Bean, @Component, or dynamically with registerBean(). While @Bean and @Component work well for static settings, registerBean() allows for more flexibility by creating beans based on user inputs or conditions at runtime. Understanding how to wire these beans together is crucial for leveraging Spring’s capabilities effectively and ensuring your application runs smoothly.