Как заинжектить все реализации одного интерфейса в сервис в Spring
JavaSpringИнтерфейсыИнъекция зависимостейРеализации интерфейсов
Что проверяют
Знаете ли вы, что Spring умеет собирать бины в коллекцию, и понимаете ли, как управлять их порядком и выбором.
Как отвечать
Достаточно попросить список или карту, контейнер соберёт все бины нужного типа сам.
@Service
public class NotificationService {
private final List<NotificationSender> senders; // все реализации
private final Map<String, NotificationSender> byName; // ключ это имя бина
public NotificationService(List<NotificationSender> senders, Map<String, NotificationSender> byName) {
this.senders = senders;
this.byName = byName;
}
}Что стоит добавить:
- порядок задаётся через
@Orderили интерфейсOrdered. Без него он не гарантирован, а для цепочки обработчиков это важно; - выбор нужной реализации по признаку: удобнее не карта по имени бина, а метод
supports(...)у каждой реализации и поиск первой подходящей. Так добавление новой реализации не требует правки сервиса; - пустой список: если реализаций нет вовсе, внедрение коллекции даст ошибку. Спасает
@Autowired(required = false)илиObjectProvider; - ленивое получение:
ObjectProvider<NotificationSender>позволяет получить бины по требованию и обойти циклические зависимости; - условная регистрация:
@ConditionalOnPropertyвключает реализацию по настройке, поэтому в разных средах набор отличается.
Это классический способ реализовать паттерн «стратегия»: интерфейс, несколько реализаций, а выбор делается по данным.
Где спотыкаются
Перечисляют реализации руками в конструкторе и правят сервис при добавлении каждой новой.
Полагаются на порядок бинов без @Order.
Кладут в карту по имени бина и завязываются на имя класса, которое меняется при рефакторинге.