Статьи

Инъекции зависимостей весной


В среде Spring есть три варианта внедрения:

  • Сеттер инъекций
  • Инжектор на основе конструктора
  • Полевая инъекция

Каждый из этих механизмов имеет свои преимущества и недостатки, и существует не только один правильный подход.
Например, полевая инъекция:
@Autowired
private FooBean fooBean;

Как правило, это не лучшая идея использовать его в производственном коде, в основном потому, что это делает невозможным тестирование наших компонентов без запуска Spring-контекста или использования рефлексов. С другой стороны, он почти не требует дополнительного кода и может быть использован в интеграционных тестах, которые определенно не будут созданы независимо. И, на мой взгляд, это единственный случай инъекций на местах.

Теперь давайте сосредоточимся на двух основных вариантах. В 
документации Spring  мы можем прочитать это


… рекомендуется использовать аргументы конструктора для обязательных зависимостей и методы установки для необязательных зависимостей.

Также в 
документации,  ссылающейся на Spring до 3.1, мы могли найти предложение


Команда Spring обычно выступает за внедрение метода установки, потому что большое количество аргументов конструктора может быть громоздким, особенно когда свойства являются необязательными.

Эта ситуация изменилась в документации до четвертой версии, которая гласит:


Команда Spring, как правило, поддерживает внедрение конструктора, поскольку оно позволяет реализовать компоненты приложения как неизменяемые объекты и гарантировать, что требуемые зависимости не равны NULL. 

Довольно круто, особенно, что до версии 4.0 люди, использующие инъекцию на основе конструктора, где просто «некоторые пуристы» (это также можно найти в 
этой документации ) ? Обратите внимание, что перед четвертым выпуском фреймворка раньше существовала большая проблема с этим вариантом внедрения. аспекты требуемого конструктора по умолчанию. Теперь есть еще один «недостаток» внедрения на основе конструктора: он не допускает циклические зависимости. Я намеренно поставил недостаток в кавычки, потому что для меня это огромное преимущество этого механизма ? Еще одно предложение из документации:


Обычно рекомендуется не полагаться на циклические ссылки между вашими компонентами. 

Но почему? Что может произойти, если в наших приложениях есть циклические ссылки? Я не хочу писать о дизайне приложений, потому что почти всегда можно реорганизовать наш код и делегировать проблемную логику третьему компоненту. Есть две значительные и, к сожалению, «тихие» проблемы.

Первая ловушка

Когда вы вызываете 
метод ListableBeanFactory.getBeansOfType ()  , вы не можете быть уверены, какие компоненты будут возвращены. Давайте посмотрим код 
 класса
DefaultListableBeanFactory :

if (isCurrentlyInCreation(bce.getBeanName())) {
  if (this.logger.isDebugEnabled()) {
    this.logger.debug("Ignoring match to currently created bean '"
        + beanName + "': " + ex.getMessage());
  }
  // ...
  continue;
}

Как вы можете видеть, если вы не используете уровень ведения журнала DEBUG, будет нулевая информация о том, что Spring пропустил конкретный компонент в процессе разрешения. Если вы хотите получить все обработчики событий, вы облажались ?

Вторая ловушка

Вторая проблема относится к АОП. Если вы хотите иметь аспект в своем бине, убедитесь, что он не вовлечен в циклическую ссылку — иначе Spring создаст два экземпляра вашего бина — один без аспекта, а другой с правильным аспектом. Конечно, все еще без какой-либо информации. Удивлены?

Для меня достаточно прекратить использование циклических зависимостей в наших приложениях (особенно из-за того, что, возможно, есть более интересные поведения, связанные с этим). Но что мы можем сделать, чтобы выйти из проблемной ситуации? Конечно, вы можете использовать инжекцию на основе конструктора ? Но если у вас огромное приложение, не стоит тратить много дней на переписывание всех классов, чтобы использовать конструкторы вместо сеттеров. К счастью, у меня есть хорошие новости —
 поле
allowCircularReferences в 
Класс AbstractRefreshableApplicationContext  . Просто добавьте одну строку к созданию контекста приложения (как описано 
в этом посте )

AnnotationConfigWebApplicationContext applicationContext =
    new AnnotationConfigWebApplicationContext();
applicationContext.setAllowCircularReferences(false);
// rest of context initialization

Наконец, для поддержания хорошего настроения я вставляю еще один фрагмент кода из 
DefaultListableBeanFactory  :

catch (NoSuchBeanDefinitionException ex) {
  // Shouldn't happen - probably a result of circular reference resolution...
  if (logger.isDebugEnabled()) {
    logger.debug("Failed to check manually registered singleton with name '"
        + beanName + "'", ex);
  }
}

Хорошего дня! ?