The IoC container - Spring Core

Spring Core is like the heart of the Spring framework. The most important thing in spring core is IoC (Inversion of Control) or DI (Dependency Injection). These concepts are the keys to unlocking how Spring works and why it's so powerful.

This blog explores how the Spring Framework implements the Inversion of Control (IoC) principle, also known as dependency injection (DI). IoC is a way of specifying what a object needs, like its dependencies, using XML, annotations, or Java code. Then, the Spring container injects these dependencies when it creates the object. This is different from the traditional way, where the object itself controls how its dependencies are created or found hence its called Inversion of Control.

In the Spring framework, we have two containers: BeanFactory and ApplicationContext. The BeanFactory is the fundamental container used for dependency injection. The ApplicationContext is a sub-interface that extends BeanFactory and provides additional features such as easier integration with Spring's AOP (Aspect-Oriented Programming) features, handling message resources for internationalization, publishing events, and supporting application-layer specific contexts like the WebApplicationContext, which is designed for use in web applications.

BeanFactory Example

Create a new Java project in Eclipse or IntelliJ, or any IDE you prefer—I'll be using Eclipse STS. Then, organize the project structure by adding JAR files, Java classes, XML files, and packages according to the following folder structure.

I have one bean class(POJO) WishMessageGenerator and it has dependency with the object of java.util.Date class.

WishMessageGenerator.java

//Spring Bean class (POJO class)
package com.hk.beans;

import java.util.Date;

public class WishMessageGenerator {

    private Date date;

    public WishMessageGenerator() {
        System.out.println("WishMessageGenerator.0-param constructor");
    }

    public String sayHello(String user) {
        return "Hello " + user + ", The time is::" + date.toString();
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }
}

We will specify/mention this dependency in applicationContext.xml file as below.

1. Here we created two beans one with id:'dt' and another with id:'wmg'.

2. the 'wmg' bean is dependent on 'dt', so we will inject it using <property> tag which basically inject the object using setter method.

3. we have used 'ref' tag, that means it will inject another bean having id:'dt'

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- Spring beans cfgs -->
    <bean id="dt" class="java.util.Date">
    </bean>
    <bean id="wmg" class="com.hk.beans.WishMessageGenerator">
        <!-- collaborators and configuration for this bean go here -->
        <property name="date" ref="dt"></property>
    </bean>
    <!-- more bean definitions go here -->
</beans>

Now that we have all the beans, lets write the main class to test the configuration.

To instruct Spring to create objects for our beans, we need to provide a configuration file as a resource. The following line of code locates the configuration file and holds all the bean data:

Resource res = new FileSystemResource("src/com/hk/cfgs/applicationContext.xml");

Next, we create the BeanFactory Container, which will handle the creation of objects for us. The BeanFactory requires the configuration file as a Resource. Here, we initialize a BeanFactory named 'factory' by passing the res resource, which holds the configuration file 'applicationContext.xml'. The 'XmlBeanFactory' class processes the XML configuration file to create and manage the beans defined within it.

The following line of code accomplishes this:

BeanFactory factory = new XmlBeanFactory(res);

Once we have the container set up, we can retrieve our objects. For instance, suppose we need an object of the WishMessageGenerator class, and the bean id is 'wmg'. The following line of code searches for the bean with id 'wmg'. When it finds the bean, it sees that there is a dependency with the 'java.util.Date' class identified by the bean id 'dt'.

Here's how it works: the code creates an object of the WishMessageGenerator class, then creates another object of the Date class, and injects it using a setter method, because in the XML configuration we used <property> tag for this dependency. Finally, it returns the object.

WishMessageGenerator generator = (WishMessageGenerator) factory.getBean("wmg");

Above line retrieves the bean named 'wmg' from the BeanFactory container and casts it to the WishMessageGenerator class. This way, we obtain an instance of the WishMessageGenerator class with all its dependencies resolved and injected.

BeanManagementTest.java

package com.hk.test;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;

import com.hk.beans.WishMessageGenerator;

public class BeanManagementTest {
    public static void main(String[] args) {
        System.out.println("stat of main(-) method");

        // variables
        WishMessageGenerator generator = null;

        // Locate and hold Spring Bean cfg file
        Resource res = new FileSystemResource("src/com/hk/cfgs/applicationContext.xml");

        // create IOC Container
        BeanFactory factory = new XmlBeanFactory(res);

        // get Bean objs from IOC container
        generator = (WishMessageGenerator) factory.getBean("wmg");
        System.out.println("Wish message::" + generator.sayHello("Hemant"));
        System.out.println("end of main(-) method");
    }
}

Output

ApplicationContext Example

Now, let's replicate the same example using the ApplicationContext container. ApplicationContext, being a sub-interface of BeanFactory, offers additional features. Therefore, we'll require an additional set of JAR files for its functionalities.

Please see below screenshot for folder structures and jars

WishMessageGenerator.java

//Spring Bean class (POJO class)
package com.hk.beans;

import java.util.Date;

public class WishMessageGenerator {

    private Date date;

    public WishMessageGenerator() {
        System.out.println("WishMessageGenerator.0-param constructor");
    }

    public String sayHello(String user) {
        return "Hello " + user + ", The time is::" + date.toString();
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }
}

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- Spring beans cfgs -->
    <bean id="dt" class="java.util.Date">
    </bean>
    <bean id="wmg" class="com.hk.beans.WishMessageGenerator">
        <!-- collaborators and configuration for this bean go here -->
        <property name="date" ref="dt"></property>
    </bean>
    <!-- more bean definitions go here -->
</beans>

BeanManagementTest.java

ClassPathXmlApplicationContext: It is one of the implementation of ApplicationContext and it tells Spring to look for a configuration file in the specified location.

"com/hk/cfgs/applicationContext.xml": This is the file where you define how your Spring application should work. It contains information about the beans and their configurations.

package com.hk.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.hk.beans.WishMessageGenerator;

public class BeanManagementTest {
    public static void main(String[] args) {
        System.out.println("stat of main(-) method");

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("com/hk/cfgs/applicationContext.xml");

        WishMessageGenerator wmg = applicationContext.getBean("wmg",WishMessageGenerator.class);
        System.out.println(wmg.sayHello("Hemant"));

        System.out.println("end of main(-) method");
    }
}

Difference between BeanFactory container and ApplicationContext container

FeatureBeanFactoryApplicationContext
Container TypeFundamental container for managing beansAdvanced container extending BeanFactory
Application TypeSuitable for standalone applicationsSuitable for web applications, AOP, ORM, etc.
Bean ScopesSupports Singleton and Prototype scopesSupports all scopes (Singleton, Prototype, Request, Session, etc.)
Annotation SupportDoes not support annotationsSupports annotation-based configuration
Messaging Functionality (i18n)Does not provide messaging functionalityExtends MessageSource for messaging support
Event PublicationDoes not support event publicationSupports event handling via ApplicationEvent
BeanPostProcessor RegistrationRequires manual registrationAutomatically registers at startup
Initialization StrategyLazy initializationEager initialization
Memory ConsumptionRequires less memoryRequires more memory

How to use multiple Configuration files?

To use multiple configuration files we use import tag like below.

<import resource="applicationContextChild.xml"/>

Example:

Create a project with below folder structure.

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- Spring beans cfgs -->
    <import resource="applicationContextChild.xml"/>
    <bean id="wmg" class="com.hk.beans.WishMessageGenerator">
        <!-- collaborators and configuration for this bean go here -->
    </bean>
    <!-- more bean definitions go here -->
</beans>

applicationContextChild.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- Spring beans cfgs -->
    <bean id="dt" class="java.util.Date">
    </bean>

    <!-- more bean definitions go here -->
</beans>

WishMessageGenerator.java

//Spring Bean class (POJO class)
package com.hk.beans;

public class WishMessageGenerator {

    public WishMessageGenerator() {
        System.out.println("WishMessageGenerator.0-param constructor");
    }

    public String sayHello(String user) {
        return "Good Morning::"+user;
    }
}

BeanManagementTest.java

package com.hk.test;

import java.util.Date;

import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;

import com.hk.beans.WishMessageGenerator;

public class BeanManagementTest {

    // spring 5.0.x doc https://docs.spring.io/spring-framework/docs/5.0.x/spring-framework-reference/

    public static void main(String[] args) {
        System.out.println("stat of main(-) method")

        GenericApplicationContext context = new GenericApplicationContext();
        new XmlBeanDefinitionReader(context).loadBeanDefinitions("com/hk/cfgs/applicationContext.xml");
        //new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
        context.refresh();
        Date date = context.getBean("dt", Date.class);
        WishMessageGenerator wmg = context.getBean("wmg",WishMessageGenerator.class);


        System.out.println(wmg.sayHello("Hemant"));
        System.out.println("date : " +date);

        System.out.println("end of main(-) method");
    }
}

Did you find this article valuable?

Support Hemant Besra by becoming a sponsor. Any amount is appreciated!