Singleton Bean Behavior - pmdacosta/notes GitHub Wiki

> Me

In the following code from a spring framework application:

ApplicationContext ctx = SpringApplication.run(Spring6idApplication.class, args);
MyController controller1 = ctx.getBean(MyController.class);
MyController controller2 = ctx.getBean(MyController.class);

Are controller1 and controller2 the same object?

> gpt

In Spring Framework, by default, the scope of a bean is singleton, meaning that there is only one instance of the bean created per Spring container (ApplicationContext). Therefore, in the code snippet you provided, controller1 and controller2 will indeed reference the same object instance.

Both ctx.getBean(MyController.class) calls will return the same singleton instance of MyController, and thus controller1 and controller2 will refer to the same object in memory. Any changes made to one controller instance will reflect in the other instance since they are essentially aliases pointing to the same object.

> Me

So how does spring create the singleton out of the following class?

@Controller
public class MyController {
    private Integer port;

    public void sayHello() {
        System.out.println("Hello from " + this.getClass().getName());
    }

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }
}

> gpt

In Spring Framework, when you annotate a class with @Controller, @Service, @Repository, or any other stereotype annotation, Spring treats it as a Spring bean and manages its lifecycle and dependencies.

When you annotate a class with @Controller, Spring automatically registers it as a bean in the Spring container. When the Spring container starts up, it scans the classpath for such annotated classes and creates instances of them as necessary.

For the MyController class you provided, since it's annotated with @Controller, Spring will create a singleton instance of this class when the application context is initialized. This means that whenever you call ctx.getBean(MyController.class) in your code, Spring will return the same instance of MyController unless you've explicitly configured it otherwise.

Here's a breakdown of how Spring creates the singleton:

  1. Scanning: When the Spring application starts up, it scans the classpath for components like controllers, services, repositories, etc.

  2. Instantiation: When Spring finds a class annotated with @Controller, it creates an instance of that class and manages its lifecycle.

  3. Singleton Scope: By default, Spring beans are singleton scoped, meaning there is only one instance of the bean in the Spring container.

  4. Lifecycle Management: Spring manages the lifecycle of the singleton bean, ensuring it is created when needed and destroyed when the application context is shut down.

Therefore, in your code snippet, both controller1 and controller2 will reference the same singleton instance of MyController. Any changes made to one instance will reflect in the other since they are essentially references to the same object.

> Me

But does the @Controller annotation generate code that turns the class into a singleton? If so, how does that code look?

> gpt

The @Controller annotation itself doesn't generate code to turn the class into a singleton. Instead, it's a special type of stereotype annotation in Spring MVC that marks a class as a controller component. It tells Spring to treat the annotated class as a controller and manage its lifecycle accordingly.

The singleton behavior in Spring is not directly tied to the @Controller annotation itself. Instead, it's a result of the default scope behavior in Spring. When a class is annotated with @Controller, it's typically managed by Spring's component scanning mechanism, and by default, Spring manages the lifecycle of such beans as singletons.

Here's an example of how Spring turns a class into a singleton:

import org.springframework.stereotype.Controller;

@Controller
public class MyController {
    private Integer port;

    public void sayHello() {
        System.out.println("Hello from " + this.getClass().getName());
    }

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }
}

When you annotate a class with @Controller like in the example above, Spring Framework essentially does the following behind the scenes:

  1. During application startup, Spring scans the classpath for components annotated with @Controller.

  2. When it finds such classes, it instantiates them using reflection or bytecode manipulation.

  3. Spring registers these instances as beans in its application context.

  4. By default, Spring assigns a singleton scope to these beans, meaning that only one instance of the bean will be created and shared throughout the application context.

So, while @Controller itself doesn't contain code to make the class a singleton, it's the default behavior of Spring to treat such annotated classes as singletons unless explicitly configured otherwise.

> me

So spring instantiates an object of the MyController class and stores it, when we call ctx.getBean(MyController.class) it returns that object, so in that sense it's using the singleton pattern because it always returns the same object, it doesn't have to change to class code to achieve the singleton pattern.

> gpt

Yes. Spring's behavior of returning the same instance of a bean whenever getBean() is called for a singleton-scoped bean is effectively implementing the singleton design pattern.

The @Controller annotation, along with other stereotype annotations like @Service, @Repository, etc., is primarily used for component scanning and to provide metadata to Spring about how to manage the annotated classes. It's the default behavior of Spring to treat such annotated classes as singletons unless specified otherwise, and this behavior achieves the singleton pattern without needing to modify the class code explicitly.

So, when you call ctx.getBean(MyController.class) multiple times within the same Spring application context, it will indeed return the same instance of MyController, effectively ensuring that only one instance of the bean exists throughout the application context's lifecycle, which aligns with the singleton pattern's characteristics.