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 .