Understanding Containers and Beans in Spring

In this article I want to go over the basics of Spring. Talk about the configuration capabilities of its beans and get a little inside.

IoC container – is a container that implements the Inversion of Control (IoC) principle. It manages the creation, binding, and life cycle of beans that are configured at various stages of the application build and then added to the context.

org.springframework.beans And org.springframework.context Packages are the basis for the Spring Framework's IoC container. BeanFactory – is a Spring container interface that provides basic functionality for creating and managing beans. BeanFactory It is used mainly for simple applications and in cases where resources are limited. It is the lowest-level interface, providing basic capabilities for configuring and managing beans. ApplicationContext is a sub-interface BeanFactoryHe adds:

  • Simplified integration with Spring AOP, allowing you to add proxying

  • Mechanisms for working with internationalizationwhich makes it easier for developers to create applications in different languages.

  • Mechanisms for publishing and handling events, which allows application components to communicate with each other (this will make application components more flexible and less coupled)

  • Our own special implementations:

    • WebApplicationContext: Extends the standard ApplicationContext and adds features needed to work with web applications, such as support for servlets, filters, and integration with web sessions

    • AnnotationConfigApplicationContext: Used for annotation-based and Java-based configuration, allowing you to work without XML configuration

    • GenericWebApplicationContext: Allows the use of both traditional XML-based configuration and annotation-based and Java-based configuration

    • ClassPathXmlApplicationContext: Allows you to configure beans based on your XML file

In short, BeanFactory provides a configuration mechanism and basic management of beans in an IoC container, while ApplicationContext extends these capabilities by offering additional features specific to enterprise applications.

The diagram below shows high level presentation of the container operation:

General idea of ​​container operation

General idea of ​​container operation

As you can see from the diagram, the Spring container requires a declaration of each bean. This declaration is described using annotations or XML. It describes the metadata for the bean configuration (from which class it should be created, whether it has an init() method and what it is called, what its properties, scope, etc. are). Spring must know the bean metadata in order to assemble it.

The Spring IoC container is completely agnostic about the format in which the configuration metadata is written. There are several ways to configure bean metadata:

  • Annotation-based configuration: mark data about the future bean for Spring directly in the class with annotations (@Component, @Service, @Repository, @Controller, @Autowired, @Qualifier)

    @Service 
    public class ExampleService {
        private int someValue;
      
        public void someMethod(){
          System.out.println("I'm a service");
        }
        public int getSomeValue() {
          return someValue;
        }
      
       public void setSomeValue(int someValue) {
         this.someValue = someValue;
       }
    }
    @Component 
    public class ExampleClass {
      private int value;
      
      @Autowired
      private ExampleService service;
      
      public void exampleMethod(){
        System.out.println("Hello world!");
        System.out.println("Service value: "+service.getSomeValue());
      }
      
      public int getValue() {
        return value;
      }
      
      public void setValue(int value) {
        this.value = value;
      }
    }
  • Java-based configuration: to configure bean metadata, you need to create a separate configuration class (annotate it with @Configuration), and register beans using a method (annotated with @Bean) that returns an object of the desired class

    @Configuration 
    public class ConfigurationClass {
      @Bean
      public ExampleService service(){
        return new ExampleService();
      }
      
      @Bean
      public ExampleClass exampleClass(){
        return new ExampleClass(service());
      }
    }
  • XML configuration: all beans and their dependencies are written in an XML file

    <!-- Определение бина ExampleService --> 
    <bean id="service" class="com.example.ExampleService"/>
    
    <!-- Определение бина ExampleClass и внедрение зависимости service -->
    <bean id="exampleClass" class="com.example.ExampleClass">
      <constructor-arg ref="service"/>
    </bean>

Each bean has its own life cycle (LC). This is the time from the moment of creation until its destruction from the context.

BeanDefenition – an object that stores information about a bin

BeanPostProcessor (BPP) – allows you to configure beans before they get into the IoC container (it manages the annotations @Autowired, @Transactional, @Async, etc.)

BeanFactoryPostProcessor (BFPP) – a class that allows you to configure a BeanDefenition (i.e. before the BeanFactory starts creating beans). It can change something in both the BeanDefenition and the BeanFactory itself before it starts working and creating beans from the BeanDefenition.

Init() метод – a method that is executed before the bean gets into the IoC container (after the first and before the second pass through all BPPs). You can write:

  • If via XML, then via the “init-method” attribute in the tag

  • If you work with annotations, then put @PostConstract

Why use init method if there is a constructor? Because the constructor is called and used by BeanFactory to create an object and perform basic data initialization. However, Spring will start to additionally configure fields (for example, BPP will start to process some annotation for a field). A problem will arise if in the constructor you try to access data that Spring configures after using the constructor itself.

In Spring, bean initialization can occur in three phases:

Three stages of bin construction

Three stages of bin construction

ApplicationListener – listens to context events and takes appropriate actions
Event types:

  • ContextStartedEvent(context started your own constructionand when it finishes, it does a refresh)

  • ContextStoppedEvent (application context is stopped)

  • ContextRefreshedEvent (the context was initialized or refreshed)

  • ContexClosedEvent (application context is closed)

Why is it needed? Example:
When starting the application, you need to “warm up” the cache (to do this, you need to go to the database, take the data and update it yourself).
You can't write logic in the constructor, because at the stage of using the constructor, the bean is not ready at all, you can't write it in the init method, because at this stage it is not yet
there is a transaction (@Transaction will not be configured, because the init method will run before BPP configures the annotation). It remains to wait for the context to be fully created and listen to the event refreshed.

Steps to create a bean:

  1. All bean declarations are read and placed into a special Map “BeanDefenitions” (bean id = declaration)

  2. BeanFactoryPostProcessor changes BeanDefenitions or BeanFactory (if configured)

  3. After creation BeanDefenitions, BeanFactory starts working on them

    1. Takes a bin

    2. Sets it up according to the configuration

    3. Sends to everyone in turn BeanPostProcessor (BPP)because they can be either custom or from spring (with annotations @Autowired, @Async, @Transactional, etc.)

    4. The object's init() method is called

    5. Another pass through all BPP (in case of proxying)

    6. Puts in IoC контейнер (if the scope of the bean is singleton)

Creating beans using XML configuration as an example

Creating beans using XML configuration as an example

Also, for each bean you can create a destroy method that will be triggered before it is destroyed. You can write:

  • If you are working with annotations, then put @PreDestroy above the method.

  • If via XML, then via the “destroy-method” attribute in the tag

Conclusion

If you want to work well, use Spring.
If you want something to work well, know its guts.

Evgeniy Borisov at the webinar “Spring the Ripper”

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *