Dev Learning Lab

The labs on this site need JavaScript to run. The same content is written out below.

Fundamentals › Spring

Beans

What is a bean, and how does it differ from an object you just created?

A bean is an object that Spring creates and keeps. In code it is an ordinary class. If you write @Component above the class, then when Spring starts it scans the app package, finds that class, creates one object, and puts it in a place for keeping objects called the container. We do not use new. Where we need the object, we ask Spring for it with context.getBean(Greeter.class), and however many times we ask, Spring gives us the same one object it was keeping. In this lab you run the same code while removing @Component or creating the object with new Greeter() instead of getBean, and see how many objects Spring created, how many objects our code created with new, and whether a == b is true or false.

Checked against real execution · Ran 4 combinations with real component scanning on Spring Framework 7.0.9, measured the number of objects, and checked against the results

Concepts in this lab

Spring bean
This is an object that the Spring IoC container creates and manages. Unlike an object that a developer creates directly with new, the container injects its dependencies and manages its initialization and destruction, and additional features such as transactions and AOP can be applied to it through a proxy.
IoC container (ApplicationContext)
This is the core Spring component that is responsible for creating beans, wiring their dependencies, and managing their lifecycle. BeanFactory is the most basic container interface, and ApplicationContext extends it and adds features such as event publication, message internationalization, and environment configuration. When people say "the container", they usually mean ApplicationContext.
@Component
@Component is an annotation that you put above a class so that Spring knows the class and treats it as something it creates objects of and manages (a bean). With component scanning, Spring scans the classes under a specified package, finds the classes with @Component, and creates objects of those classes and keeps them in the container. If you do not give the bean a name, the class name with its first letter in lowercase is used (greeter for Greeter). @Repository, @Service, and @Controller are specializations of @Component, so they are found in the same way. A class without this annotation is not found even if it is in the same package, so if you ask Spring for an object of that class, an exception saying that there is no such bean is thrown.
Component scanning
This feature finds the classes under a given package that carry @Component or one of its related annotations (such as @Service, @Repository, and @Controller) and registers them as beans automatically. In Spring Boot, the default target is the package of the class annotated with @SpringBootApplication and its subpackages.

Try these

Running with the default settings

  1. The main method runs, and the line new AnnotationConfigApplicationContext("app") starts Spring. When it starts, the container is created. The container is the place where Spring keeps the objects it creates, and for now it is empty.
  2. Spring scans the classes in the app package and looks for classes with @Component. It is on the Greeter class.
  3. Spring creates one object of the Greeter class and keeps it in the container. An object that Spring creates and keeps like this is a bean. This object is already created when Spring starts, before our code asks for it. The code we wrote has no line new Greeter().
  4. The line Greeter a = context.getBean(Greeter.class); runs. It asks Spring for a Greeter object. Spring does not create a new one and gives us the object it was keeping in the container. The variable a points to that object. The number of objects created by Spring is still 1.
  5. The line Greeter b = context.getBean(Greeter.class); runs. Even when we ask once more, Spring does not create a new one and gives us the same object. This is because Spring creates only one bean and gives us that one every time we ask. The variable b points to the same object as a.
  6. The line System.out.println(a.hello()); runs. The hello() method of the object that a points to returns "Hello", and this text is printed.
  7. The line System.out.println(a == b); runs. a == b is an expression that compares whether the two variables point to the same object. a and b point to the same object kept in the container, so true is printed.

Result of this run

Code

// Mark: Spring creates an object of this class and keeps it
@Component
public class Greeter {
    public String hello() {
        return "Hello";
    }
}

public class Main {
    public static void main(String[] args) {
        // Start Spring. It scans the app package for @Component
        ApplicationContext context =
                new AnnotationConfigApplicationContext("app");
        // Ask for the Greeter object that Spring is keeping
        Greeter a = context.getBean(Greeter.class);
        Greeter b = context.getBean(Greeter.class);
        System.out.println(a.hello());
        System.out.println(a == b);
    }
}

Assumptions of this model

These are the results of running Spring Framework 7.0.9 on JDK 21.0.12.1. Spring Boot was not used, and only AnnotationConfigApplicationContext("app") was used, as in the code on the screen. All 4 combinations were run as they are, by compiling a source with the same structure as the code on the screen (Greeter and Main in the package app) and starting a new Java process. Greeter was not registered by hand, and Spring was made to scan the app package and find @Component. What was checked is the text that the program printed (Hello and the value of a == b), the kind of exception when it stopped with an exception, the number of Greeter objects created by Spring, and the number of objects created by our code with new. The numbers of objects were measured, not guessed. Only in the source used for checking, a line that increases a number each time an object is created was added to the constructor of Greeter, and the count right after Spring started was recorded as the number created by Spring, and the count at the end minus that count was recorded as the number our code created with new. The count right after starting was already 1 (when @Component is present) before getBean was called, so Spring creates the object when Spring starts, not when getBean is called. Calling getBean twice did not increase the count and a == b was true, and in the combinations that use new, the number of Greeter beans in the container was the same at the end as right after starting, so objects created with new did not go into the container. When @Component was absent, the count right after starting was 0 and there were no registered names in the container (Spring's internal ones were excluded), and at the getBean line org.springframework.beans.factory.NoSuchBeanDefinitionException (the message says there is no bean of type app.Greeter) occurred, the process ended with an error code, and nothing was printed. When @Component was present, there was one registered name in the container, greeter, and that bean was a singleton. What the documentation specifies is the following sections of the Core part of the Spring Framework documentation. Introduction to the Spring IoC Container and Beans (a bean is an object that is created, assembled, and managed by the Spring IoC container, and an object that is not is just one of the many objects in the application), Classpath Scanning and Managed Components (@Component is a generic annotation for a component managed by Spring, and @Service, @Controller, and others are specializations of it; Spring finds annotated classes and registers bean definitions; and if no name is given, the class name with its first letter in lowercase is used), Bean Scopes (singleton is the default scope, and the container creates exactly one object for that bean definition and returns that object every time a request comes), and the API documentation of AnnotationConfigApplicationContext(String... basePackages) (it finds components in the given packages, registers bean definitions, and refreshes the context automatically). Unlike what the documentation specifies, that the object is created at startup, that a class without @Component is not created even if getBean is called, and the name of the exception are behaviors observed on this version. The code on the screen leaves out the package and import lines, and assumes that Greeter and Main are both in the app package. The line that closes the container was not used. Besides @Component, there are other ways to register a bean, such as a @Bean method, and @Service and @Controller, which belong to the @Component family, beans whose scope is not singleton, and dependency injection, which connects beans to each other, are not covered. They are covered in the in-depth lab "The Spring container and beans".

Previous lab: Libraries and dependencies · Next lab: Dependency injection

Concepts in this lab

What this lab shows

Settings

Code


      
    

Diagram

Run comparison

Try these