Differenza tra BeanFactory e ApplicationContext

1. Panoramica

Spring Framework viene fornito con due contenitori IOC: BeanFactory e ApplicationContext . Il BeanFactory è più semplice versione di contenitori CIO, e l'ApplicationContext estende le caratteristiche di BeanFactory .

In questo rapido tutorial, capiremo le differenze significative tra questi due contenitori IOC con esempi pratici.

2. Caricamento pigro e caricamento desideroso

BeanFactory carica i bean su richiesta, mentre ApplicationContext carica tutti i bean all'avvio . Pertanto, BeanFactory è leggero rispetto ad ApplicationContext . Capiamolo con un esempio.

2.1. Caricamento pigro con BeanFactory

Supponiamo di avere una classe di bean singleton chiamata Student con un metodo:

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

Ci definiamo la postConstruct () Metodo come init metodo nel nostro BeanFactory file di configurazione, CIO-contenitore-differenza-example.xml :

Ora, scriviamo un test case che crei un BeanFactory per verificare se carica il bean Student :

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

Qui, l' oggetto Student non è inizializzato . In altre parole, viene inizializzato solo BeanFactory . I bean definiti nella nostra BeanFactory verranno caricati solo quando chiameremo esplicitamente il metodo getBean () .

Controlliamo l'inizializzazione del nostro bean Student dove chiamiamo manualmente il metodo 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()); }

Qui, il bean Student viene caricato correttamente. Quindi, BeanFactory carica il fagiolo solo quando è necessario.

2.2. Caricamento desideroso con ApplicationContext

Ora usiamo ApplicationContext al posto di BeanFactory.

Definiremo solo ApplicationContext e caricherà immediatamente tutti i bean utilizzando una strategia di caricamento desideroso:

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

Qui, l' oggetto Student viene creato anche se non abbiamo chiamato il metodo getBean () .

ApplicationContext è considerato un contenitore IOC pesante perché la sua strategia di caricamento desideroso carica tutti i bean all'avvio. BeanFactory è leggero al confronto e potrebbe essere utile nei sistemi con vincoli di memoria. Tuttavia, vedremo nelle prossime sezioni perché ApplicationContext è preferito per la maggior parte dei casi d'uso .

3. Caratteristiche dell'applicazione aziendale

ApplicationContext migliora BeanFactory in uno stile più orientato al framework e fornisce diverse funzionalità adatte alle applicazioni aziendali.

Per esempio, fornisce messaggistica (i18n o di internazionalizzazione) la funzionalità, la pubblicazione evento funzionalità, l'iniezione di dipendenza annotazioni a base , e una facile integrazione con Spring AOP caratteristiche .

Oltre a questo, ApplicationContext supporta quasi tutti i tipi di bean scope, ma BeanFactory supporta solo due ambiti: Singleton e Prototype . Pertanto, è sempre preferibile utilizzare ApplicationContext quando si creano applicazioni aziendali complesse.

4. Registrazione automatica di BeanFactoryPostProcessor e BeanPostProcessor

L'ApplicationContext registra automaticamente BeanFactoryPostProcessor e BeanPostProcessor all'avvio. D'altra parte, BeanFactory non registra automaticamente queste interfacce.

4.1. Registrazione in BeanFactory

Per capire, scriviamo due classi.

In primo luogo, abbiamo la classe CustomBeanFactoryPostProcessor , che implementa BeanFactoryPostProcessor :

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

Qui, abbiamo sovrascritto il metodo postProcessBeanFactory () per controllarne la registrazione.

In secondo luogo, abbiamo un'altra classe, CustomBeanPostProcessor , che 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 }

Qui, abbiamo sovrascritto il metodo postProcessBeforeInitialization () per controllarne la registrazione.

Inoltre, abbiamo configurato entrambe le classi nel nostro file di configurazione ioc-container-difference-example.xml :

Vediamo un test case per verificare se queste due classi vengono registrate automaticamente all'avvio:

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

Come possiamo vedere dal nostro test, la registrazione automatica non è avvenuta .

Ora, vediamo un caso di test che li aggiunge manualmente in 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()); }

Qui, abbiamo utilizzato il metodo postProcessBeanFactory () per registrare CustomBeanFactoryPostProcessor e il metodo addBeanPostProcessor () per registrare CustomBeanPostProcessor . Entrambi si registrano correttamente in questo caso.

4.2. Registrazione in ApplicationContext

Come abbiamo notato in precedenza, ApplicationContext registra automaticamente entrambe le classi senza scrivere codice aggiuntivo.

Verifichiamo questo comportamento in uno 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.