Какие способы внедрения зависимостей существуют в Spring сервисах и как инжектить репозиторий
Что проверяют
Знаете ли вы, чем способы внедрения отличаются на практике, а не только синтаксисом, и почему в современном Spring выбирают конструктор. Следом обычно спрашивают, что будет, если бинов нужного типа два, и откуда вообще берётся реализация у интерфейса репозитория.
Как отвечать
Назовите три способа с оценкой:
- через конструктор: основной вариант. Поля можно сделать
final, объект не создать без зависимостей, в тесте сервис собирается обычнымnewс моками. Если конструктор один,@Autowiredнад ним не нужен начиная со Spring 4.3; часто конструктор генерирует Lombok через@RequiredArgsConstructor; - через сеттер: для необязательных зависимостей или тех, что меняются после создания;
- через поле:
@Autowiredпрямо над полем. Коротко, но поле неfinal, зависимость скрыта от читателя, а в юнит-тесте без контекста Spring её придётся подсовывать рефлексией.
Вместо @Autowired можно использовать @Inject из jakarta.inject и @Resource, который ищет бин по имени, а не по типу.
Про репозиторий: в Spring Data вы объявляете интерфейс, например UserRepository extends JpaRepository<User, Long>, а реализацию на старте создаёт сам Spring в виде прокси поверх SimpleJpaRepository. Аннотация @Repository на таком интерфейсе не обязательна. В сервис он приходит так же, как любой бин: поле private final UserRepository userRepository и конструктор.
Если реализаций несколько, помогает @Qualifier с именем бина или @Primary на основной. Необязательную зависимость удобно получать через ObjectProvider<T> или Optional<T>.
Где спотыкаются
Уверяют, что без @Autowired на конструкторе внедрение не сработает. Это давно не так, и лишняя аннотация выдаёт знания по старым туториалам.
Пишут класс-реализацию для интерфейса Spring Data или создают сервис через new в другом классе и удивляются, что репозиторий внутри равен null: объект, созданный вручную, контейнер не видит.
Не знают про циклические зависимости. С конструктором цикл ломает старт приложения, а в Spring Boot начиная с 2.6 циклы по умолчанию запрещены. Правильная реакция: пересмотреть дизайн, а не переходить на внедрение через поле ради обхода.