In the last post, we talked about the Spring Framework. Now that we have understood what Spring is, we would discuss more about the IoC container, which is the most important part of Spring.

The Spring IoC Container

At the core of Spring lies the Inversion of Control (IoC) principle, where control is shifted from the application to the IoC container. A special form of IoC, Dependency Injection (DI), allows objects to define their dependencies through constructor arguments, factory method arguments, or set properties. The IoC container then injects these dependencies during bean creation.

The two main packages which form the basis of IoC in Spring are

  • org.springframework.beans
  • org.springframework.context

The Spring IoC container is responsible for instantiating, configuring, and assembling the objects and is represented by the org.springframework.context.ApplicationContext interface. The BeanFactory interface provides the basic framework to manage any type of object (Beans), and the ApplicationContext adds more functionalities to it. Instructions are provided to the container about the objects using configurations, and the configuration metadata can be defined in the following formats :

  • XML-based Metadata,
  • Java Annotations-based Metadata,
  • and Java Code-based Metadata

The application classes (POJOs) are combined with the configuration metadata, and once ApplicationContext is created and initialized, we have a configured and executable system.

Instantiating the Spring Container

We can provide a path of the configuration metadata to the ApplicationContext constructor using a string parameter.

ApplicationContext context = new ClassPathXmlApplicationContext("configuration_metadata.xml");

Spring provides us with various implementations of the ApplicationContext interface. These are :

  • ClassPathXmlApplicationContext
  • FileSystemXmlApplicationContext

Using the Spring Container

The ApplicationContext maintains data about the beans registered with it and their dependencies, and we can use the getBean(String name, Class< T > requiredType) method to retrieve a bean of the required object type.

// initialize the application context
ApplicationContext context = new ClassPathXmlApplicationContext("config_file.xml");

// retrive instance of object
HelloWorldService service = context.getBean("helloWorld", HelloWorldService.class);

// use the instance
String string = service.getHelloWorld();

Spring Beans

In Spring, a Bean is an object that is instantiated, assembled, and managed by the Spring IoC container. Throughout the lifecycle from instantiation to management, every step is guided by the configuration metadata provided to the Spring container. The container uses a BeanDefinition object to represent a bean within the container which acts as a blueprint for the bean.

Every bean needs one or more identifiers, and these should be unique within the container hosting the bean. These identifiers can be specified in the configuration metadata using various attributes such as “id” and “name”.

Instantiating Beans

As mentioned earlier, the container uses the blueprint provided using configuration metadata to create an object for the requested bean.

  1. Instantiation with a Constructor

    With the constructor approach, the class under development is not required to implement any specific interface or conform to a predetermined coding structure. Simply putting the required bean class in the constructor should be sufficient.

     public class HelloWorld {
    
         // the HelloWorld class has a dependency on Country class
         private final Country country;
    
         // a constructor so that the Spring container can inject a Country object
         public HelloWorld(Country country) {
             this.country = country;
         }
    
     // business logic...
     }
    
  2. Instantiation with a Static Factory Method

    To instantiate a bean with a factory method, we use a class attribute to specify the class that contains the static factory method and an attribute named factory-method to specify the factory method. The static factory method then can be invoked to get objects of the required type.

     <bean id="countryService"
         class="examples.CountryService"
         factory-method="createInstance"/>
    

    Below is how we can use static factory method to get an object of the bean defined in the XML above.

     public class CountryService {
    
         private static CountryService countryService = new CountryService();
            
         private CountryService() {}
    
         public static CountryService countryService() {
             return countryService;
         }
     }
    
  3. Instantiation by Using an Instance Factory Method

    This method is similar to the static factory method, but instead of invoking a static method we invoke a non-static factory method. We leave the class attribute empty and use factory-bean attribute to define the name of the bean that contains the instance method that would create the object, and factory-method attribute to define the name of the factory method.

     <!-- the factory bean, which contains a method to create instance of the required bean -->
     <bean id="helloWorld" class="examples.HelloWorld">
         <!-- inject any dependencies required by this bean -->
     </bean>
    
     <!-- the bean to be created via the factory bean -->
     <bean id="countryService"
         factory-bean="helloWorld"
         factory-method="createInstance"/>
    

    Below is how we can use instance factory method to get an object of the bean defined in the XML above.

     public class HelloWorld {
    
         private static CountryService countryService = new CountryService();
    
         public CountryService createInstance() {
             return countryService;
         }
     }
    

Lazy Initialization of Beans

When ApplicationContext is initialized, it creates and configures all the singleton beans. This early initialization is done so that any errors can be encountered immediately rather than later after initialization. For all other types of beans that do not require immediate initialization, we can use lazy-initialization which tells the IoC container to create an instance of the bean only when it is first requested.

The lazy-init attribute can be used to set any bean to be lazily initialized, as shown in XML below :

<beans default-lazy-init="true">
    <!-- no beans will be initialized at startup -->

    <!-- set lazy initialization as true -->
    <bean id="helloWorld" class="examples.HelloWorld" lazy-init="true"/>
    <bean id="countryService" class="examples.CountryService">
</beans>

We can also use the default-lazy-init attribute on the beans element to set the default behavior. Lazy-initialization is used to improve the startup time for the application by making sure that only the required beans are created at the startup of the ApplicationContext.

In this post, we got to know about the Spring IoC container, how it manages the Spring Beans, what are Spring Beans, and how we can instantiate them!

References

  • Spring Framework Docs