В этой статье рассмотрим аннотации @Primary, @Qualifier. А также, как внедрить список бинов в другой бин.
Пример
Допустим, в Spring приложении есть несколько бинов , реализующих интерфейс IAnimalRepository:
public interface IAnimalRepository { void save(); }
Пусть это два бина: AnimalRepository1 и AnimalRepository2:
@Repository public class AnimalRepository1 implements IAnimalRepository { @Override public void save() { System.out.println("AnimalRepository 1 save"); } }
@Repository public class AnimalRepository2 implements IAnimalRepository { @Override public void save() { System.out.println("AnimalRepository 2 save"); } }
Мы внедряем бин типа IAnimalRepository в другой бин — AnimalService:
@Service public class AnimalService { private IAnimalRepository animalRepository; @Autowired public AnimalService(IAnimalRepository animalRepository){ this.animalRepository=animalRepository; } public void save(){ animalRepository.save(); } }
Возникает вопрос, какой именно бин внедрит Spring. Spring не знает, какую реализацию мы имеем в виду. Если ее не уточнить, то возникнет ошибка:
Parameter 0 of constructor in ru.sysout.AnimalService required a single bean, but 2 were found: - animalRepository1: defined in file [C:\Code\sysout\spring-core-primary\target\classes\ru\sysout\AnimalRepository1.class] - animalRepository2: defined in file [C:\Code\sysout\spring-core-primary\target\classes\ru\sysout\AnimalRepository2.class]
А ниже — пояснение:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
Spring подсказывает, что аннотации @Primary и @Qualifier позволяют уточнить конкретную реализацию.
Аннотация @Primary
Аннотация @Primary задает бин, который будет внедрен по умолчанию (при отсутствии других указаний).
Например, пометим AnimalRepository1 аннотацией @Primary:
@Repository @Primary public class AnimalRepository1 implements IAnimalRepository { @Override public void save() { System.out.println("AnimalRepository 1 save"); } }
Теперь ошибка исчезнет — в AnimalService будет внедрен AnimalRepository1.
Аннотация @Qualifier
Аннотация @Qualifier позволяет уточнить имя бина, который надо внедрить. Используется прямо перед аргументом.
В этот раз давайте скажем Spring внедрить бин AnimalRepository2. Для этого укажем в аннотации @Qualifier имя бина.
@Service public class AnimalService { private IAnimalRepository animalRepository; @Autowired public AnimalService(@Qualifier("animalRepository2") IAnimalRepository animalRepository){ this.animalRepository=animalRepository; } public void save(){ animalRepository.save(); } }
На этот раз Spring внедрит бин AnimalRepository2.
Кстати, аннотация @Primary над AnimalRepository1 случайно осталась. Но аннотация @Qualifier имеет больший приоритет, чем @Primary, поэтому все равно внедряется AnimalRepository2.
Внедрение списка
Наконец, создадим еще один сервис ListAnimalService и внедрим в него оба репозитория — списком. Все бины типа IAnimalRepository будут внедрены в поле list:
@Service public class ListAnimalService { private List<IAnimalRepository> list; @Autowired public ListAnimalService(List<IAnimalRepository> list) { this.list = list; } public void save() { list.forEach(repo -> repo.save()); } }
Проверка
Вызов методов save() сервисов (а из них save() репозиториев с выводом номера репозитория) делаем в @PostConstruct главного класса:
@SpringBootApplication public class MainApplication { @Autowired private AnimalService animalService; @Autowired private ListAnimalService listAnimalService; @PostConstruct public void test() { animalService.save(); listAnimalService.save(); } public static void main(String[] args) { SpringApplication.run(MainApplication.class, args); } }
Исходный код
Исходный код примера доступен на GitHub.
Можно ещё уточнить, что при внедрении бинов в коллекцию можно указывать очередность, если это важно для нас — через аннотации @Order или @Priority на бинах.