Почему лучше использовать инъекцию через конструктор, а не через поля в Spring
Что проверяют
Понимаете ли вы разницу между «работает» и «не даёт выстрелить в ногу», и знаете ли, что конкретно ломается при инъекции в поля. Следом идёт вопрос про циклические зависимости и про то, как протестировать такой бин без контекста.
Как отвечать
Через конструктор зависимости становятся частью контракта класса, через поля они спрятаны и подставляются рефлексией уже после создания объекта. Отсюда весь список различий.
finalи неизменяемость: поля, заполняемые конструктором, объявляютсяfinal, объект после создания консистентен. Поле с@Autowiredфинальным быть не может по определению;- невозможное состояние: бин без обязательной зависимости не соберётся на старте. При полевой инъекции объект создаётся, а
NullPointerExceptionприлетает при первом обращении, иногда через месяц; - тестируемость:
new OrderService(repo, clock)с заглушками работает без Spring вообще. Для полей нужен контекст или@InjectMocksс рефлексией, и тест становится медленнее и хрупче; - честный сигнал о дизайне: конструктор на семь аргументов выглядит уродливо, и это правильно, он показывает, что класс делает слишком много. В списке полей те же семь зависимостей глаз не режут;
- циклы: конструкторная инъекция ловит цикл при старте, полевая даёт ему дожить до боя. В Spring Boot начиная с 2.6 циклы запрещены по умолчанию, а
spring.main.allow-circular-references=trueэто заплатка, а не решение.
Добавьте практику: с Spring 4.3 у класса с единственным конструктором аннотация @Autowired не нужна, а Lombok @RequiredArgsConstructor над классом с private final полями убирает бойлерплейт. Для необязательных зависимостей остаются сеттеры и ObjectProvider.
Где спотыкаются
Отвечают одним словом «тестируемость» и замолкают. Это верно, но это лишь один пункт из пяти, и следующий вопрос будет про остальные.
Ставят @Autowired на конструктор и уверяют, что без аннотации бин не соберётся. Для единственного конструктора она давно не нужна.
Разрывают цикл через @Lazy или флаг, не разобравшись в причине. Цикл почти всегда значит, что два класса делят ответственность, которую надо вынести в третий.