1. Обзор
Фреймворк Spring поставляется с двумя контейнерами IOC – BeanFactory и ApplicationContext . BeanFactory является самой базовой версией контейнеров IOC, а 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-difference-example.xml :
Теперь давайте напишем тестовый случай, который создает BeanFactory , чтобы проверить, загружает ли он Student been:
@Test
public void whenBFInitialized_thenStudentNotInitialized() {
Resource res = new ClassPathResource("ioc-container-difference-example.xml");
BeanFactory factory = new XmlBeanFactory(res);
assertFalse(Student.isBeanInstantiated());
}Здесь объект Student не инициализируется . Другими словами, инициализируется только 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());
}Здесь компонент Student успешно загружается. Следовательно, BeanFactory загружает боб только тогда, когда это необходимо.
2.2. Нетерпеливая Загрузка С Помощью ApplicationContext
Теперь давайте использовать ApplicationContext вместо BeanFactory.
Мы определим только ApplicationContext, и он мгновенно загрузит все компоненты, используя стратегию нетерпеливой загрузки:
@Test
public void whenAppContInitialized_thenStudentInitialized() {
ApplicationContext context = new ClassPathXmlApplicationContext("ioc-container-difference-example.xml");
assertTrue(Student.isBeanInstantiated());
}Здесь, в Студент объект создается, даже если мы не вызывали getBean() метод.
ApplicationContext считается тяжелым контейнером IOC, поскольку его стратегия нетерпеливой загрузки загружает все компоненты при запуске. BeanFactory является легким по сравнению и может быть удобен в системах с ограниченным объемом памяти. Тем не менее, мы увидим в следующих разделах, почему ApplicationContext предпочтительнее для большинства случаев использования .
3. Функции корпоративного приложения
ApplicationContext расширяет BeanFactory в более ориентированном на фреймворк стиле и предоставляет несколько функций, подходящих для корпоративных приложений.
Например, он обеспечивает обмен сообщениями (i18n или интернационализацию) функциональность, публикацию событий функциональность, внедрение зависимостей на основе аннотаций и легкую интеграцию с функциями Spring AOP .
Кроме того, ApplicationContext поддерживает почти все типы областей bean, но BeanFactory поддерживает только две области — Singleton и Prototype . Поэтому всегда предпочтительнее использовать ApplicationContext при создании сложных корпоративных приложений.
4. Автоматическая регистрация BeanFactoryPostProcessor и BeanPostProcessor
ApplicationContext автоматически регистрирует BeanFactoryPostProcessor и BeanPostProcessor при запуске. С другой стороны, BeanFactory не регистрирует эти интерфейсы автоматически.
4.1. Регистрация в BeanFactory
Чтобы понять, давайте напишем два класса.
Во-первых, у нас есть Пользовательский класс BeanFactoryPostProcessor , который реализует BeanFactoryPostProcessor :
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
private static boolean isBeanFactoryPostProcessorRegistered = false;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory){
setBeanFactoryPostProcessorRegistered(true);
}
// standard setters and getters
}Здесь мы переопределили метод postProcessBeanFactory () , чтобы проверить его регистрацию.
Во-вторых, у нас есть другой класс, Custom BeanPostProcessor , который реализует 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-difference-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());
}Как мы видим из нашего теста, автоматической регистрации не произошло .
Теперь давайте рассмотрим тестовый случай, который вручную добавляет их в Бобовый завод :
@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() для регистрации пользовательского BeanFactoryPostProcessor и метод addBeanPostProcessor() для регистрации пользовательского BeanPostProcessor . Оба они успешно регистрируются в этом случае.
4.2. Регистрация в ApplicationContext
Как мы уже отмечали ранее, ApplicationContext регистрирует оба класса автоматически без написания дополнительного кода.
Давайте проверим это поведение в модульном тесте:
@Test
public void whenAppContInitialized_thenBFPostProcessorAndBPostProcessorRegisteredAutomatically() {
ApplicationContext context
= new ClassPathXmlApplicationContext("ioc-container-difference-example.xml");
assertTrue(CustomBeanFactoryPostProcessor.isBeanFactoryPostProcessorRegistered());
assertTrue(CustomBeanPostProcessor.isBeanPostProcessorRegistered());
}Как мы видим, автоматическая регистрация обоих классов в этом случае успешна .
Поэтому всегда рекомендуется использовать ApplicationContext , поскольку Spring 2.0 (и выше) в значительной степени использует BeanPostProcessor.
Также стоит отметить, что если вы используете обычный BeanFactory, то такие функции, как транзакции и AOP, не вступят в силу (по крайней мере, без написания дополнительных строк кода). Это может привести к путанице, потому что в конфигурации ничего не будет выглядеть неправильно.
5. Заключение
В этой статье мы рассмотрели ключевые различия между ApplicationContext и BeanFactory на практических примерах.
ApplicationContext поставляется с расширенными функциями, в том числе несколькими, ориентированными на корпоративные приложения, в то время как BeanFactory поставляется только с базовыми функциями. Поэтому обычно рекомендуется использовать ApplicationContext, и мы должны использовать BeanFactory только тогда, когда потребление памяти критично .
Как всегда, код для статьи доступен на GitHub .