Перейти к основному содержимому

Разница между BeanFactory и ApplicationContext

· 5 мин. чтения

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 .