Diferencia entre BeanFactory y ApplicationContext

1. Información general

Spring Framework viene con dos contenedores IOC: BeanFactory y ApplicationContext . El BeanFactory es la versión más básica de contenedores COI, y la ApplicationContext amplía las características de BeanFactory .

En este tutorial rápido, entenderemos las diferencias significativas entre estos dos contenedores IOC con ejemplos prácticos.

2. Carga diferida frente a carga ansiosa

BeanFactory carga beans a pedido, mientras que ApplicationContext carga todos los beans al inicio . Por lo tanto, BeanFactory es liviano en comparación con ApplicationContext . Entendamos con un ejemplo.

2.1. Carga diferida con BeanFactory

Supongamos que tenemos una clase de bean singleton llamada Student con un método:

public class Student { public static boolean isBeanInstantiated = false; public void postConstruct() { setBeanInstantiated(true); } //standard setters and getters }

Vamos a definir la PostConstruct () método que el init-método en nuestra BeanFactory archivo de configuración, COI-contenedor-diferencia-example.xml :

Ahora, escriba un caso de prueba que cree un BeanFactory para verificar si carga el bean Student :

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

Aquí, el objeto Student no se inicializa . En otras palabras, solo se inicializa BeanFactory . Los beans definidos en nuestra BeanFactory se cargarán solo cuando llamemos explícitamente al método getBean () .

Comprobemos la inicialización de nuestro bean Student donde llamamos manualmente al método 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()); }

Aquí, el bean Student se carga correctamente. Por lo tanto, BeanFactory solo carga el bean cuando es necesario.

2.2. Carga ansiosa con ApplicationContext

Ahora, usemos ApplicationContext en lugar de BeanFactory.

Solo definiremos ApplicationContext, y cargará todos los beans instantáneamente usando una estrategia de carga ansiosa:

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

Aquí, el objeto Student se crea aunque no hayamos llamado al método getBean () .

ApplicationContext se considera un contenedor IOC pesado porque su estrategia de carga ansiosa carga todos los beans al inicio. BeanFactory es liviano en comparación y podría ser útil en sistemas con restricciones de memoria. Sin embargo, veremos en las siguientes secciones por qué se prefiere ApplicationContext para la mayoría de los casos de uso .

3. Funciones de la aplicación empresarial

ApplicationContext mejora BeanFactory en un estilo más orientado al marco y proporciona varias características que son adecuadas para aplicaciones empresariales.

Por ejemplo, proporciona funcionalidad de mensajería (i18n o internacionalización) , funcionalidad de publicación de eventos , inyección de dependencia basada en anotaciones y fácil integración con las funciones de Spring AOP .

Aparte de esto, ApplicationContext admite casi todos los tipos de ámbitos de frijol, pero BeanFactory solo admite dos ámbitos: Singleton y Prototype . Por lo tanto, siempre es preferible utilizar ApplicationContext al crear aplicaciones empresariales complejas.

4. Registro automático de BeanFactoryPostProcessor y BeanPostProcessor

El Application Context registra automáticamente BeanFactoryPostProcessor y BeanPostProcessor en el arranque. Por otro lado, BeanFactory no registra estas interfaces automáticamente.

4.1. Registro en BeanFactory

Para entender, escribamos dos clases.

En primer lugar, tenemos la clase CustomBeanFactoryPostProcessor , que implementa el BeanFactoryPostProcessor :

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

Aquí, hemos anulado el método postProcessBeanFactory () para verificar su registro.

En segundo lugar, tenemos otra clase, CustomBeanPostProcessor , que implementa 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 }

Aquí, hemos anulado el método postProcessBeforeInitialization () para verificar su registro.

Además, hemos configurado ambas clases en nuestro archivo de configuración ioc-container-difference-example.xml :

Veamos un caso de prueba para comprobar si estas dos clases se registran automáticamente durante el inicio:

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

Como podemos ver en nuestra prueba, el registro automático no sucedió .

Ahora, veamos un caso de prueba que los agrega manualmente en 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()); }

Aquí, usamos el método postProcessBeanFactory () para registrar CustomBeanFactoryPostProcessor y el método addBeanPostProcessor () para registrar CustomBeanPostProcessor . Ambos se registran con éxito en este caso.

4.2. Registro en ApplicationContext

Como notamos anteriormente, ApplicationContext registra ambas clases automáticamente sin escribir código adicional.

Verifiquemos este comportamiento en una prueba unitaria:

@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.