Spring: где ставить Autowired



Аннотацию Autowired можно ставить над конструктором, сеттером и просто над полем.



Есть популярная рекомендация, что через конструктор — самое правильное. На эту тему написаны сотни(!) статей и ответов на StackOverflow. Даже Intellij IDEA подсказывает, что "Field injection is not recommended"



В большинстве моих проектов Autowired ставили над полем, потому что так короче. В этом посте разберёмся, откуда взялась эта рекомендация, и насколько она актуальна.



В чём разница между способами с точки зрения Spring?



Только в порядке установки для каждого бина:

Конструктор→поля→сеттеры



В остальном механизмы спринга работают одинаково:

▫️ Циклические зависимости возможны везде

▫️ Прокси создаются корректно

▫️ Отсутствующие зависимости обнаруживаются при компиляции или на старте приложения



Откуда взялась эта рекомендация?



Большинство авторов ссылаются на документацию. В разделе Dependency Injection указаны только два способа — конструктор и сеттер. Они универсальны и для XML конфигурации, и для аннотаций.



В этом же разделе стоит рекомендация — использовать конструктор для обязательных и final полей, сеттер — для необязательных.



Autowired над полями относится только к annotation-based конфигурации, поэтому находится в другом разделе. В документации нет ни единого упоминания, что Autowired над полями — это грех и хуже конструкторов и сеттеров.



Рассмотрим основные аргументы против Autowired над полем:



😱 Слишком просто добавлять новые зависимости. Класс легко может потерять единственную ответственность. А когда в конструкторе 10 параметров, это сразу заметно

💁 Единственная ответственность Autowired — внедрить зависимость. Следить за дизайном — задача программиста



😱 Зависимость от DI-контейнера. Класс с аннотациями нельзя использовать за пределами проекта

💁 Ни разу не было такой потребности



😱 Полям нельзя добавить final

💁 Жаль, конечно, но мало кто в рантайме заменяет сервисы и репозитории



😱 Autowired поля проставляются через Reflection

💁 Если вам важно, что делает фреймворк под капотом, посмотрите на класс Constructor Resolver, там рефлекшена в 10 раз больше



😱 Непонятно, какие зависимости обязательные, а какие нет

💁 По умолчанию все Autowired зависимости обязательные. Если что-то необязательно, то можно поставить флажок (required=false)



😱 Непонятно, как тестировать такие классы без поднятия контекста

💁 10 лет назад было действительно никак. Сейчас юнит-тесты легко писать с помощью Mockito аннотаций @InjectMocks и @MockBean



Итого



Autowired над полями — самый лаконичный способ внедрить зависимость. Я не нашла ни одного весомого аргумента против. Для типичного энтерпрайз приложения этот способ идеально подходит.



Лучшие практики и рекомендации формируются в своём контексте. Когда контекст меняется, практика может стать неактуальной. И это норм🙂