1. Overview

The Spring Framework comes with two IOC containers – BeanFactory and ApplicationContext. The BeanFactory is the most basic version of IOC containers, and the ApplicationContext extends the features of BeanFactory.

In this quick tutorial, we’ll understand the significant differences between these two IOC containers with practical examples.

2. Lazy Loading vs. Eager Loading

BeanFactory loads beans on-demand, while ApplicationContext loads all beans at startup. Thus, BeanFactory is lightweight as compared to ApplicationContext. Let’s understand it with an example.

2.1. Lazy Loading With BeanFactory

Let’s suppose we have a singleton bean class called Student with one method:

public class Student {
    public static boolean isBeanInstantiated = false;

    public void postConstruct() {
        setBeanInstantiated(true);
    }

    //standard setters and getters
}

We’ll define the postConstruct() method as the init-method in our BeanFactory configuration file, ioc-container-difference-example.xml:

<bean id="student" class="com.baeldung.ioccontainer.bean.Student" init-method="postConstruct"/>

Now, let’s write a test case that creates a BeanFactory to check if it loads the Student bean:

@Test
public void whenBFInitialized_thenStudentNotInitialized() {
    Resource res = new ClassPathResource("ioc-container-difference-example.xml");
    BeanFactory factory = new XmlBeanFactory(res);
    
    assertFalse(Student.isBeanInstantiated());
}

Here, the Student object is not initialized. In other words, only the BeanFactory is initialized. The beans defined in our BeanFactory will be loaded only when we explicitly call the getBean() method*.*

Let’s check the initialization of our Student bean where we’re manually calling the getBean() method:

@Test
public void whenBFInitialized_thenStudentInitialized() {
    Resource res = new ClassPathResource("ioc-container-difference-example.xml");
    BeanFactory factory = new XmlBeanFactory(res);
    Student student = (Student) factory.getBean("student");

    assertTrue(Student.isBeanInstantiated());
}

Here, the Student bean loads successfully. Hence, the BeanFactory only loads the bean when it is required.

2.2. Eager Loading With ApplicationContext

Now, let’s use ApplicationContext in the place of BeanFactory.

We’ll only define ApplicationContext, and it will load all the beans instantly by using an eager-loading strategy:

@Test
public void whenAppContInitialized_thenStudentInitialized() {
    ApplicationContext context = new ClassPathXmlApplicationContext("ioc-container-difference-example.xml");
    
    assertTrue(Student.isBeanInstantiated());
}

Here, the Student object is created even though we have not called the getBean() method.

ApplicationContext is considered a heavy IOC container because its eager-loading strategy loads all the beans at startup. BeanFactory is lightweight by comparison and could be handy in memory-constrained systems. Nevertheless, we’ll see in the next sections why ApplicationContext is preferred for most use cases.

3. Enterprise Application Features

ApplicationContext enhances BeanFactory in a more framework-oriented style and provides several features that are suitable for enterprise applications.

For instance, it provides messaging (i18n or internationalization) functionality, event publication functionality, annotation-based dependency injection, and easy integration with Spring AOP features.

Apart from this, the ApplicationContext supports almost all types of bean scopes, but the BeanFactory only supports two scopes — Singleton and Prototype. Therefore, it’s always preferable to use ApplicationContext when building complex enterprise applications.

4. Automatic Registration of BeanFactoryPostProcessor and BeanPostProcessor

The ApplicationContext* automatically registers BeanFactoryPostProcessor and *BeanPostProcessor at startup. On the other hand, the BeanFactory does not register these interfaces automatically.

4.1. Registration in BeanFactory

To understand, let’s write two classes.

Firstly, we have the CustomBeanFactoryPostProcessor class, which implements the BeanFactoryPostProcessor:

public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    private static boolean isBeanFactoryPostProcessorRegistered = false;
    
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory){
        setBeanFactoryPostProcessorRegistered(true);
    }

    // standard setters and getters
}

Here, we’ve overridden the postProcessBeanFactory() method to check its registration.

Secondly, we have another class, CustomBeanPostProcessor, which implements BeanPostProcessor:

public class CustomBeanPostProcessor implements BeanPostProcessor {
    private static boolean isBeanPostProcessorRegistered = false;
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName){
        setBeanPostProcessorRegistered(true);
        return bean;
    }

    //standard setters and getters
}

Here, we’ve overridden the postProcessBeforeInitialization() method to check its registration.

Also, we’ve configured both the classes in our ioc-container-difference-example.xml configuration file:

<bean id="customBeanPostProcessor" 
  class="com.baeldung.ioccontainer.bean.CustomBeanPostProcessor" />
<bean id="customBeanFactoryPostProcessor" 
  class="com.baeldung.ioccontainer.bean.CustomBeanFactoryPostProcessor" />

Let’s see a test case to check whether these two classes are registered automatically during startup:

@Test
public void whenBFInitialized_thenBFPProcessorAndBPProcessorNotRegAutomatically() {
    Resource res = new ClassPathResource("ioc-container-difference-example.xml");
    ConfigurableListableBeanFactory factory = new XmlBeanFactory(res);

    assertFalse(CustomBeanFactoryPostProcessor.isBeanFactoryPostProcessorRegistered());
    assertFalse(CustomBeanPostProcessor.isBeanPostProcessorRegistered());
}

As we can see from our test, automatic registration did not happen.

Now, let’s see a test case that manually adds them in the BeanFactory:

@Test
public void whenBFPostProcessorAndBPProcessorRegisteredManually_thenReturnTrue() {
    Resource res = new ClassPathResource("ioc-container-difference-example.xml");
    ConfigurableListableBeanFactory factory = new XmlBeanFactory(res);

    CustomBeanFactoryPostProcessor beanFactoryPostProcessor 
      = new CustomBeanFactoryPostProcessor();
    beanFactoryPostProcessor.postProcessBeanFactory(factory);
    assertTrue(CustomBeanFactoryPostProcessor.isBeanFactoryPostProcessorRegistered());

    CustomBeanPostProcessor beanPostProcessor = new CustomBeanPostProcessor();
    factory.addBeanPostProcessor(beanPostProcessor);
    Student student = (Student) factory.getBean("student");
    assertTrue(CustomBeanPostProcessor.isBeanPostProcessorRegistered());
}

Here, we used the postProcessBeanFactory() method to register CustomBeanFactoryPostProcessor and the addBeanPostProcessor() method to register CustomBeanPostProcessor. Both of them register successfully in this case.

4.2. Registration in ApplicationContext

As we noted earlier, ApplicationContext registers both the classes automatically without writing additional code.

Let’s verify this behavior in a unit test:

@Test
public void whenAppContInitialized_thenBFPostProcessorAndBPostProcessorRegisteredAutomatically() {
    ApplicationContext context 
      = new ClassPathXmlApplicationContext("ioc-container-difference-example.xml");

    assertTrue(CustomBeanFactoryPostProcessor.isBeanFactoryPostProcessorRegistered());
    assertTrue(CustomBeanPostProcessor.isBeanPostProcessorRegistered());
}

As we can see, automatic registration of both classes is successful in this case.

Therefore, it’s always advisable to use ApplicationContext because Spring 2.0 (and above) heavily uses BeanPostProcessor.

It’s also worth noting that if you’re using the plain BeanFactory, then features like transactions and AOP will not take effect (at least not without writing extra lines of code). This may lead to confusion because nothing will look wrong with the configuration.

5. Conclusion

In this article, we’ve seen the key differences between ApplicationContext and BeanFactory with practical examples.

The ApplicationContext comes with advanced features, including several that are geared towards enterprise applications, while the BeanFactory comes with only basic features. Therefore, it’s generally recommended to use the ApplicationContext, and we should use BeanFactory only when memory consumption is critical.

As always, the code for the article is available over on GitHub.