1. The introduction
The Spring framework ships with two IOC containers — the BeanFactory and the ApplicationContext. BeanFactory is the most basic version of the IOC container, and the ApplicationContext extends the functionality of the BeanFactory. In this article, we’ll look at the significant differences between these two IOC containers through a practical example.
Lazy loading vs. preloading
The BeanFactory loads beans on demand, while the ApplicationContext loads all beans at startup. Therefore, the BeanFactory is lightweight compared to ApplicationContext. Let’s use an example to understand it.
2.1. BeanFactoryLazy loading
Suppose we have a singleton Bean named Student:
public class Student {
public static boolean isBeanInstantiated = false;
public void postConstruct(a) {
setBeanInstantiated(true);
}
//standard setters and getters
}
Copy the code
We’ll define the postConstruct() method as init method in the ioc-container-difference-example.xml BeanFactory configuration file:
<bean id="student" class="com.baeldung.ioccontainer.bean.Student" init-method="postConstruct"/>
Copy the code
Now, let’s write a test case to create a BeanFactory to check if it loads the Student bean:
@Test
public void whenBFInitialized_thenStudentNotInitialized(a) {
Resource res = new ClassPathResource("ioc-container-difference-example.xml");
BeanFactory factory = new XmlBeanFactory(res);
assertFalse(Student.isBeanInstantiated());
}
Copy the code
Here, the Student object is not initialized. In other words, only the BeanFactory is initialized. The bean defined in the *BeanFactory will only be loaded if we explicitly call the *getBean() method. Let’s check the initialization of the Student bean by manually calling the getBean() method:
@Test
public void whenBFInitialized_thenStudentInitialized(a) {
Resource res = new ClassPathResource("ioc-container-difference-example.xml");
BeanFactory factory = new XmlBeanFactory(res);
Student student = (Student) factory.getBean("student");
assertTrue(Student.isBeanInstantiated());
}
Copy the code
Here, the Student bean loads successfully. Therefore, BeanFactory loads beans only when needed.
2.2. ApplicationContextpreload
Now, instead of BeanFactory, let’s just define ApplicationContext, which will load all beans at once using a preload strategy:
@Test
public void whenAppContInitialized_thenStudentInitialized(a) {
ApplicationContext context = new ClassPathXmlApplicationContext("ioc-container-difference-example.xml");
assertTrue(Student.isBeanInstantiated());
}
Copy the code
Here, even if we didn’t call the getBean() method, creating the Student object ApplicationContext is considered a heavy IOC container because its preloading policy loads all beans at startup. By contrast, the BeanFactory is lightweight and handy on memory-constrained systems. Nevertheless, most use cases still prefer to use ApplicationContext. Why?
3. Enterprise application functionality
ApplicationContext enhances the BeanFactory in a more framework-oriented style and provides some functionality suitable for enterprise applications.
For example, it provides messaging (I18N or internationalization) capabilities, event publishing capabilities, annotation based dependency injection, and simple integration with Spring AOP features.
In addition, the ApplicationContext supports almost all types of bean scopes, but the BeanFactory supports only two scopes — Singleton and Prototype. Therefore, it is best to use the ApplicationContext when building complex enterprise applications.
4. Automatic registrationBeanFactoryPostProcessorandBeanPostProcessor
**ApplicationContext automatically registers BeanFactoryPostProcessor and BeanPostProcessor at startup **. However, BeanFactory does not automatically register these interfaces.
4.1. Register in BeanFactory
To understand, let’s write two classes. First of all, we have CustomBeanFactoryPostProcessor class, it implements the spring BeanFactoryPostProcessor:
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
private static boolean isBeanFactoryPostProcessorRegistered = false;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory){
setBeanFactoryPostProcessorRegistered(true);
}
// standard setters and getters
}
Copy the code
Here, we override the postProcessBeanFactory() method to check its registration. Second, 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
}
Copy the code
Here, we rewrite the PostProcessBeforeAlization () method to check the registration. In addition, we configure these two classes in the 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" />
Copy the code
Let’s look at a test case to check whether the two classes are automatically registered during startup:
@Test
public void whenBFInitialized_thenBFPProcessorAndBPProcessorNotRegAutomatically(a) {
Resource res = new ClassPathResource("ioc-container-difference-example.xml");
ConfigurableListableBeanFactory factory = new XmlBeanFactory(res);
assertFalse(CustomBeanFactoryPostProcessor.isBeanFactoryPostProcessorRegistered());
assertFalse(CustomBeanPostProcessor.isBeanPostProcessorRegistered());
}
Copy the code
From our tests, we can see that automatic registration did not happen. Now, let’s look at a test case that manually adds them to the BeanFactory:
@Test
public void whenBFPostProcessorAndBPProcessorRegisteredManually_thenReturnTrue(a) {
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());
}
Copy the code
Here, we use postProcessBeanFactory registered CustomBeanFactoryPostProcessor () method, Register the CustomBeanPostProcessor with the addBeanPostProcessor() method. In this case, they all register successfully.
In 4.2.ApplicationContextRegistered in
As mentioned earlier, ApplicationContext automatically registers both classes without writing additional code. Let’s verify this behavior in a unit test:
@Test
public void whenAppContInitialized_thenBFPostProcessorAndBPostProcessorRegisteredAutomatically(a) {
ApplicationContext context
= new ClassPathXmlApplicationContext("ioc-container-difference-example.xml");
assertTrue(CustomBeanFactoryPostProcessor.isBeanFactoryPostProcessorRegistered());
assertTrue(CustomBeanPostProcessor.isBeanPostProcessorRegistered());
}
Copy the code
We can see that the automatic registration of both classes was successful. Therefore, it is recommended to use ApplicationContext because Spring2.0 (and later) makes heavy use of BeanPostProcessor. It’s also worth noting that if you’re using a plain BeanFactory, features like transactions and AOP won’t work (unless you write extra code to implement them, which is another story). This can lead to messy code because the configuration seems to be fine.
5. Write at the end
ApplicationContext provides some advanced functionality, including some for enterprise applications, while BeanFactory provides only basic functionality. Therefore, it is generally recommended to use ApplicationContext, and we should only consider using the BeanFactory if memory consumption is critical.