В этой статье рассмотрим аннотации @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 на бинах.