В чем разница в отказоустойчивости приложения при инициализации бинов через конструктор и через сеттер в Spring?
Что проверяют
Понимаете ли вы, когда Spring обнаружит недостающую зависимость: на старте контекста или уже на живом запросе. Следом обычно спрашивают про циклические зависимости и про то, почему документация Spring советует конструктор для обязательных зависимостей.
Как отвечать
Сравните два способа по моменту, когда проявится ошибка:
- конструктор: объект нельзя создать без всех аргументов. Нет подходящего бина, и контекст падает при старте с
UnsatisfiedDependencyException, приложение просто не поднимется. Поля можно сделатьfinal, объект сразу целостен и неизменяем. Единственный конструктор не требует@Autowiredначиная со Spring 4.3; - сеттер: объект создаётся пустым, зависимость ставится позже. Сеттер с
@Autowiredпо умолчанию тоже обязателен (required = true), поэтому внутри контейнера ошибка тоже всплывёт на старте. Опасность в другом: приrequired = false, при создании черезnewв тестах или вне контейнера поле остаётсяnull, иNullPointerExceptionприлетает в рантайме, иногда через часы работы; - циклы: если A зависит от B, а B от A через конструкторы, Spring не сможет создать ни один и сообщит об этом при старте. Сеттеры цикл технически позволяют, но со Spring Boot 2.6 циклические ссылки по умолчанию запрещены (
spring.main.allow-circular-references=false), и цикл обычно признак неудачного разбиения.
Итог для ответа: конструктор даёт fail fast, ошибка конфигурации ловится на деплое, а не у пользователя. Сеттер оставьте для по-настоящему опциональных или перенастраиваемых зависимостей, а для необязательного бина ещё удобнее ObjectProvider<T> в конструкторе.
Где спотыкаются
Утверждают, что с сеттером Spring вообще не заметит отсутствия бина. Для @Autowired на сеттере это неверно: он обязателен по умолчанию, и контекст упадёт так же, как с конструктором.
Путают final у поля и у конструктора. Конструктор final быть не может, неизменяемость дают поля с final, которые присваиваются в конструкторе.
Хвалят инъекцию в поле как самую короткую. Она прячет зависимости, не даёт final и без контейнера или рефлексии не тестируется, так что риски у неё те же, что у сеттера.