Какие способы инжекции зависимостей существуют в Spring
Что проверяют
Знаете ли вы три способа и понимаете ли, почему рекомендуется конструктор.
Как отвечать
Способов три.
Через конструктор (рекомендуемый):
@Service
class JobService {
private final JobRepository repo;
JobService(JobRepository repo) { this.repo = repo; }
}С одним конструктором аннотация @Autowired не нужна, Spring подставит сам. Плюсы: поля можно объявить final, значит объект неизменяем и гарантированно полностью собран; зависимости видны в сигнатуре, и разросшийся список сам сигнализирует, что класс делает слишком много; в тесте объект создаётся обычным new без контейнера.
Через сеттер: подходит для необязательных зависимостей и для того, что можно переопределить после создания.
Через поле (@Autowired прямо на поле): самый компактный и самый проблемный. Поле не может быть final, зависимости спрятаны, а без контейнера объект не собрать, поэтому в тесте приходится лезть через рефлексию. Spring помечает такой способ как не рекомендуемый.
Что ещё стоит назвать: @Qualifier и @Primary для выбора среди нескольких реализаций одного интерфейса; внедрение коллекции всех реализаций (List<Handler>); @Value и @ConfigurationProperties для конфигурации; ObjectProvider для ленивого получения.
Важное следствие: циклическая зависимость через конструктор падает на старте, а через поле раньше молча работала. Начиная с Spring Boot 2.6 циклы запрещены по умолчанию, и это правильно, потому что цикл почти всегда означает неверную границу между классами.
Где спотыкаются
Ставят @Autowired на поля и потом не могут написать модульный тест.
Не знают, зачем нужен final.
Разрешают циклическую зависимость через @Lazy вместо разделения классов.
Внедряют бин с областью видимости запроса в синглтон и получают одно значение на всё приложение.