Разлика между BeanFactory и ApplicationContext

1. Общ преглед

Spring Framework се предлага с два IOC контейнера - BeanFactory и ApplicationContext . В BeanFactory е най-основната версия на МОК контейнери и ApplicationContext разширява функциите на BeanFactory .

В този бърз урок ще разберем значителните разлики между тези два IOC контейнера с практически примери.

2. Мързеливо зареждане срещу нетърпеливо зареждане

BeanFactory зарежда зърна при поискване, докато ApplicationContext зарежда всички зърна при стартиране . По този начин BeanFactory е лек в сравнение с ApplicationContext . Нека го разберем с пример.

2.1. Мързеливо зареждане с BeanFactory

Да предположим, че имаме клас на единичен боб, наречен Student с един метод:

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

Ще определим метода postConstruct () като init-метод в нашия конфигурационен файл BeanFactory , ioc-container- razlika -example.xml :

Сега, нека напишем тестов случай, който създава BeanFactory, за да провери дали зарежда боб Student :

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

Тук, в Студентски обекта не се инициализира . С други думи, само BeanFactory се инициализира . Фасулът, определени в нашия BeanFactory ще бъдат заредени само когато сме изрично да извиквате getBean () метод .

Нека проверим инициализацията на нашия Student bean, където ръчно извикваме метода 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()); }

Тук студентският боб се зарежда успешно. Следователно BeanFactory зарежда зърното само когато е необходимо.

2.2. Нетърпеливо зареждане с ApplicationContext

Сега, нека използваме ApplicationContext на мястото на BeanFactory.

Ще дефинираме само ApplicationContext и той ще зареди всички зърна незабавно, като използва стратегия за зареждане с нетърпение:

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

Тук се създава обектът Student, въпреки че не сме извикали метода getBean () .

ApplicationContext се счита за тежък контейнер на IOC, тъй като неговата стратегия за зареждане зарежда всички зърна при стартиране. BeanFactory е лек за сравнение и може да бъде удобен в системи с ограничена памет. Независимо от това, ще видим в следващите раздели защо ApplicationContext е предпочитан за повечето случаи на употреба .

3. Характеристики на корпоративното приложение

ApplicationContext подобрява BeanFactory в по-ориентиран към рамката стил и предоставя няколко функции, подходящи за корпоративни приложения.

Например, той предоставя функционалност за съобщения (i18n или интернационализация), функционалност за публикуване на събития, инжектиране на зависимости , основано на анотации , и лесна интеграция с функциите Spring AOP .

Отделно от това, ApplicationContext поддържа почти всички видове обхват на зърна, но BeanFactory поддържа само два обхвата - Singleton и Prototype . Следователно, винаги е за предпочитане да се използва ApplicationContext при изграждане на сложни корпоративни приложения.

4. Автоматична регистрация на BeanFactoryPostProcessor и BeanPostProcessor

В ApplicationContext автоматично регистрира BeanFactoryPostProcessor и BeanPostProcessor при стартиране. От друга страна, BeanFactory не регистрира тези интерфейси автоматично.

4.1. Регистрация в BeanFactory

За да разберем, нека напишем два класа.

Първо, имаме класа CustomBeanFactoryPostProcessor , който реализира BeanFactoryPostProcessor :

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

Тук сме заменили метода postProcessBeanFactory () , за да проверим регистрацията му.

На второ място, имаме друг клас, CustomBeanPostProcessor , който реализира 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 }

Тук сме заменили метода postProcessBeforeInitialization () , за да проверим регистрацията му.

Също така сме конфигурирали и двата класа в нашия конфигурационен файл ioc-container-razlika-example.xml :

Нека да видим тестов случай, за да проверим дали тези два класа се регистрират автоматично по време на стартиране:

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

Както виждаме от нашия тест, автоматичната регистрация не се случи .

Сега нека видим тестов случай, който ръчно ги добавя в 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()); }

Тук използвахме метода postProcessBeanFactory () , за да регистрираме CustomBeanFactoryPostProcessor и метода addBeanPostProcessor () , за да регистрираме CustomBeanPostProcessor . И двамата се регистрират успешно в този случай.

4.2. Регистрация в ApplicationContext

Както отбелязахме по-рано, ApplicationContext регистрира и двата класа автоматично, без да пише допълнителен код.

Нека проверим това поведение в единичен тест:

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