Aamir Rizwan
Aamir Rizwan

Reputation: 887

Conditionally auto-wire a bean implementation based on a value?

I have an interface Animal having two implementations Cat and Dog. These two implementations are spring @Component. How do I conditionally wire these two based on a value? I understand that I may have to change the scope of MyTestController from singleton to request.

@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;

    }

}

Upvotes: 0

Views: 628

Answers (2)

Ph03n1x
Ph03n1x

Reputation: 844

Since both MyTestController is a bean the autowiring / initialisation happens before you actually start using the class instance itself. What I mean is that by the time you actually trigger REST requests on your controller, the injected animal bean should be already there!

More specifically if you have two classes that implement the same interface (Animal) without further specification (active Profiles, or @Primary annotation) Spring won't be able to decide which implementation to inject while creating the MyTestController,

What you want to do is return beans from your ApplicationContext based on a parameter / class name. This would look something like this:

@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
}

Edit IMO the question is a bit confusing. You ask for wiring the bean based on a value, but this value only comes at runtime. Hence my answer. However If you want to wire based on some variable at initialisation of your bean I would suggest taking a look at the following sources:

  • Profiles - With profiles you can tell spring which instance to inject in which configuration. (E.g.: production/development/test configs and for each you want to inject different beans)
  • Primary - One of your bean takes precedence over the others while injecting it.
  • Qualifier

Finally I would note that such an inversion on the IoC is considered as a bad practice. (See here)

Upvotes: 1

Michał Stochmal
Michał Stochmal

Reputation: 6620

Well, you're missing the whole point. Don't autowire simple DTO, but autowire AnimalFactory or some kind of AnimalRepository (or better - Service) where you can create or retrieve animals based on animal type.

It would look something like that:

@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 Data JPA Repository:

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

  public Optional<Animal> findByAnimalName(String animalName);

}

Upvotes: 0

Related Questions