BeanFactory vs ApplicationContext: When to Choose Each in Spring

Spring provides two IoC containers—BeanFactory and ApplicationContext—each with distinct loading strategies, features, and registration behaviors; this article demonstrates their differences through practical code examples, highlighting lazy versus eager loading, automatic post‑processor registration, and why ApplicationContext is generally preferred for enterprise applications.

Programmer DD
Programmer DD
Programmer DD
BeanFactory vs ApplicationContext: When to Choose Each in Spring

1. Introduction

Spring framework includes two IoC containers – BeanFactory and ApplicationContext . BeanFactory is the most basic version, while ApplicationContext extends its functionality. This article uses concrete examples to illustrate the significant differences between the two containers.

2. Lazy Loading vs. Preloading

BeanFactory loads beans on demand, whereas ApplicationContext loads all beans at startup. Consequently, BeanFactory is lightweight compared to ApplicationContext. Let’s understand this with an example.

2.1 BeanFactory Lazy Loading

Assume we have a singleton bean named Student :

public class Student {
    public static boolean isBeanInstantiated = false;

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

    // standard setters and getters
}

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

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

Now we write a test case that creates a BeanFactory and checks whether the Student bean is loaded:

@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 itself is initialized. The bean is created only when we explicitly call getBean() :

@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());
}

Now the Student bean is successfully loaded, demonstrating that BeanFactory loads beans only when needed.

2.2 ApplicationContext Preloading

Replacing BeanFactory with ApplicationContext triggers eager loading of all beans at startup:

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

    assertTrue(Student.isBeanInstantiated());
}

Even without calling getBean() , the Student object is created. ApplicationContext is considered a heavyweight IoC container because its preloading strategy loads all beans during startup. BeanFactory remains lightweight and is convenient for memory‑constrained systems, but most use cases prefer ApplicationContext.

3. Enterprise Application Features

ApplicationContext enhances BeanFactory with a more framework‑oriented style and provides features useful for enterprise applications, such as internationalization (i18n), event publishing, annotation‑based dependency injection, and simple integration with Spring AOP.

Moreover, ApplicationContext supports virtually all bean scopes, whereas BeanFactory only supports singleton and prototype scopes. Therefore, for complex enterprise applications, ApplicationContext is the better choice.

4. Automatic Registration of BeanFactoryPostProcessor and BeanPostProcessor

ApplicationContext automatically registers BeanFactoryPostProcessor and BeanPostProcessor at startup. BeanFactory does not.

4.1 Registering in BeanFactory

We create two classes: CustomBeanFactoryPostProcessor implementing BeanFactoryPostProcessor , and CustomBeanPostProcessor implementing BeanPostProcessor :

public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    private static boolean isBeanFactoryPostProcessorRegistered = false;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        setBeanFactoryPostProcessorRegistered(true);
    }
}
public class CustomBeanPostProcessor implements BeanPostProcessor {
    private static boolean isBeanPostProcessorRegistered = false;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        setBeanPostProcessorRegistered(true);
        return bean;
    }
}

Both classes are declared in ioc-container-difference-example.xml :

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

A test verifies that BeanFactory does not automatically register them:

@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());
}

We can manually register them:

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

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

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

4.2 Registering in ApplicationContext

ApplicationContext automatically registers both processors without extra code. A test confirms this behavior:

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

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

Thus, automatic registration works, and using ApplicationContext is recommended because Spring 2.0 and later heavily rely on BeanPostProcessor. Note that with a plain BeanFactory, features such as transactions and AOP will not work unless additional code is written.

5. Conclusion

ApplicationContext offers advanced, enterprise‑oriented capabilities, while BeanFactory provides only basic functionality. Generally, ApplicationContext should be used, reserving BeanFactory for scenarios where memory consumption is extremely critical.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaIoCspringdependency-injectionapplicationcontextBeanFactory
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.