@Primary, @Qualifier и внедрение списка

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

 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *