1. Обзор
Spring Framework поставляется с двумя контейнерами IOC — BeanFactory
и ApplicationContext
. BeanFactory — это
самая базовая версия контейнеров IOC, а ApplicationContext
расширяет возможности BeanFactory
.
В этом кратком руководстве мы поймем существенные различия между этими двумя контейнерами IOC на практических примерах.
2. Ленивая загрузка против нетерпеливой загрузки
BeanFactory
загружает компоненты по запросу, а ApplicationContext
загружает все компоненты при запуске . Таким образом, BeanFactory легче
по сравнению с ApplicationContext
. Давайте разберемся на примере.
2.1. Ленивая загрузка с BeanFactory
Предположим, у нас есть одноэлементный класс bean-компонента 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
:
<bean id="student" class="com.foreach.ioccontainer.bean.Student" init-method="postConstruct"/>
Теперь давайте напишем тестовый пример, который создает BeanFactory
, чтобы проверить, загружает ли он компонент Student :
@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
, где мы вручную вызываем метод 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,
и он мгновенно загрузит все bean-компоненты, используя стратегию быстрой загрузки:
@Test
public void whenAppContInitialized_thenStudentInitialized() {
ApplicationContext context = new ClassPathXmlApplicationContext("ioc-container-difference-example.xml");
assertTrue(Student.isBeanInstantiated());
}
Здесь объект Student
создается, несмотря на то, что мы не вызывали метод getBean()
.
ApplicationContext
считается тяжелым контейнером IOC, поскольку его стратегия быстрой загрузки загружает все bean-компоненты при запуске. BeanFactory
по сравнению с ним легковесен и может быть удобен в системах с ограниченным объемом памяти. Тем не менее, в следующих разделах мы увидим, почему ApplicationContext
предпочтительнее для большинства случаев использования .
3. Возможности корпоративного приложения
ApplicationContext
улучшает BeanFactory
в более ориентированном на структуру стиле и предоставляет несколько функций, подходящих для корпоративных приложений.
Например, он обеспечивает функциональность обмена сообщениями (i18n или интернационализация) , функциональность публикации событий , внедрение зависимостей на основе аннотаций и простую интеграцию с функциями Spring AOP . **
**
Помимо этого, ApplicationContext
поддерживает почти все типы областей видимости bean-компонентов, но 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-difference-example.xml
:
<bean id="customBeanPostProcessor"
class="com.foreach.ioccontainer.bean.CustomBeanPostProcessor" />
<bean id="customBeanFactoryPostProcessor"
class="com.foreach.ioccontainer.bean.CustomBeanFactoryPostProcessor" />
Давайте посмотрим тестовый пример, чтобы проверить, регистрируются ли эти два класса автоматически во время запуска:
@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());
}
Как видим, автоматическая регистрация обоих классов в данном случае проходит успешно.
Поэтому всегда рекомендуется использовать ApplicationContext
, поскольку Spring 2.0 (и выше) активно использует BeanPostProcessor.
Также стоит отметить, что если вы используете простую BeanFactory,
то такие функции, как транзакции и АОП, не будут работать (по крайней мере, без написания дополнительных строк кода). Это может привести к путанице, потому что в конфигурации ничего не будет выглядеть неправильно.
5. Вывод
В этой статье мы рассмотрели ключевые различия между ApplicationContext
и BeanFactory
на практических примерах.
ApplicationContext поставляется с расширенными функциями, включая несколько, предназначенных для корпоративных приложений, в то время как BeanFactory
имеет
только базовые функции. Поэтому обычно рекомендуется использовать ApplicationContext,
а BeanFactory
следует использовать только тогда, когда потребление памяти критично `` .
Как всегда, код статьи доступен на GitHub .