Условно автоматическое подключение реализации bean-компонента на основе значения?

У меня есть интерфейс Animal с двумя реализациями Cat и Dog. Эти две реализации - пружина @Component. Как мне условно связать эти два на основе значения? Я понимаю, что мне, возможно, придется изменить область действия MyTestController с одноэлементного на запрос.

@RestController
public class MyTestController {

    @Autowired
    Animal animal;// how to wire bean of Cat or Dog based on animalName

    @PostMapping("/get-animal")
    public @ResponseBody Animal getAnimal(@RequestParam(value = "animalName") String animalName) {
        return animal;

    }

}

Основан ли этот профиль реализации или вы явно укажете, какой из них использовать?

Mehraj Malik 16.08.2018 08:30

Не проводное подключение. Просто найдите тот, который вам нужен, в своем методе

M. Deinum 16.08.2018 08:32

Возможный дубликат Можно ли условно объявить Spring bean?

Mehraj Malik 16.08.2018 08:34

@MehrajMalik Не на основе профиля.

Aamir Rizwan 16.08.2018 08:42

Я согласен с М. Дейнумом, за исключением того, что я бы предпочел: автосоединяйте их оба или автосоединяйте List <Animal> и ищите тот, который вам нужен, внутри метода.

JB Nizet 16.08.2018 08:53
0
5
394
2

Ответы 2

Поскольку оба MyTestController являются bean-компонентами, происходит автоматическое подключение / инициализация перед, вы фактически начинаете использовать сам экземпляр класса. Я имею в виду, что к тому времени, когда вы действительно запускаете запросы REST на своем контроллере, внедренный bean-компонент animal уже должен быть там!

В частности, если у вас есть два класса, реализующие один и тот же интерфейс (Animal) без дополнительной спецификации (активные профили или аннотация @Primary), Spring не сможет решить, какую реализацию внедрить при создании MyTestController,

Что вы хотите сделать, так это вернуть bean-компоненты из вашего ApplicationContext на основе имени параметра / класса. Это выглядело бы примерно так:

@Autowired
private ApplicationContext context;

/* ... */
if (animalName.equals("dog") {
  context.getBean(Dog.class) //returning the dog bean
} else if (animalName.equals("cat") {
  context.getBean(Cat.class) //returning the cat bean
}

Редактировать IMO вопрос немного сбивает с толку. Вы просите связать bean-компонент на основе значения, но это значение появляется только во время выполнения. Отсюда и мой ответ. Однако, если вы хотите подключиться к какой-либо переменной при инициализации вашего bean-компонента, я бы предложил взглянуть на следующие источники:

  • Профили - с помощью профилей вы можете указать Spring, какой экземпляр в какую конфигурацию вводить. (Например: конфигурации производства / разработки / тестирования, и для каждого вы хотите внедрить разные bean-компоненты)
  • Начальный - один из ваших bean-компонентов имеет приоритет над другими при инъекции.
  • Квалификатор

Напоследок отмечу, что такая инверсия на IoC считается плохой практикой. (См. здесь)

Что ж, вы упускаете из виду весь смысл. Не подключайте простой DTO автоматически, а подключайте AnimalFactory или какой-нибудь AnimalRepository (или лучше - Service), где вы можете создавать или извлекать животных в зависимости от их типа.

Это выглядело бы примерно так:

@Component
public class AnimalFactory {

  public Animal createAnimal(String animalType) {
     switch (animalType) {
       case "DOG":
        return new Dog();
       case "CAT":
        return new Cat();
     }
     throw new IllegalArgumentException("AnimalType is invalid");
  }

}

Репозиторий данных Animal Spring JPA:

@Component
public class AnimalRepository implements JpaRepository<Animal, Long> {

  public Optional<Animal> findByAnimalName(String animalName);

}

Другие вопросы по теме