«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.baeldung.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.baeldung.ioccontainer.bean.CustomBeanPostProcessor" />
<bean id="customBeanFactoryPostProcessor" 
  class="com.baeldung.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 только тогда, когда потребление памяти критично.