Dynamic instance injection via CDI

In the Java EE platform with CDI, it is possible to introduce instances of POJO classes. Very simply, we need to use the @Inject annotation to enter the default instance of some interface. We can also use qualifiers to enter a specific class in our field. But these solutions are pretty static.

I need another dynamic model of injection material.

Let me introduce my problem: Let's say that we have an Animal interface and three classes that implement this interface: Ant, Dog, Elephant. I would like to dynamically insert an instance of one of these three classes, and it depends on some variable, such as string (animal name). In Java SE, I would do this as shown below:

Map<String, Animal> animalMap = new HashMap<>(); animalMap.put("ant", new Ant()); animalMap.put("dog", new Dog()); animalMap.put("elephant", new Elephant()); ... String animalName = ...; Animal animal = animalMap.get(animalMap); animal.doSomething(); 

I need something like:

 class AnimalManager { @Inject // ? private Animal animal; // ? public void run(String animalName) { // based on animalName get new instance of animal and run doSomething() ... animal.doSomething(); // if animalName is "ant" call the doSomething on Ant class } } 

In all classes implementing the Animal interface, I need to use variables with @EJB annotations.

What is the best and most correct way to do this in Java EE?

EDIT:
OK, based on a response from Svetlin Zarev and hwellmann (thanks!) I created this:

First we create an Animal model:

 @Qualifier @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.TYPE}) public @interface AnimalModel { Type value(); enum Type { ANT, DOG, ELEPHANT } } 

Let me create an Animal:

 public interface Animal { public void eat(Object food); } 

Next, specific classes:

 @AnimalModel(AnimalModel.Type.ANT) public class Ant implements Animal { @Override public void eat(Object food) { ... } } @AnimalModel(AnimalModel.Type.DOG) public class Dog implements Animal { @Override public void eat(Object food) { ... } } @AnimalModel(AnimalModel.Type.ELEPHANT) public class Elephant implements Animal { @Override public void eat(Object food) { ... } } 

Next, AnimalLiteral:

 public class AnimalLiteral extends AnnotationLiteral<AnimalModel> implements AnimalModel { private static final long serialVersionUID = 1L; private Type type; public AnimalLiteral(Type type) { this.type = type; } public Type value() { return type; } } 

The main component is Animal factory:

 @Dependent public class AnimalFactory { @Inject @Any private Instance<Animal> animals; private static Map<String, AnimalModel.Type> animalMap; public AnimalFactory() { animalMap = new HashMap<>(); animalMap.put("ant", AnimalModel.Type.ANT); animalMap.put("dog", AnimalModel.Type.DOG); animalMap.put("elephant", AnimalModel.Type.ELEPHANT); } public Animal getAnimal(String animalName) { AnimalModel.Type type = animalMap.get(animalName); AnimalLiteral literal = new AnimalLiteral(type); Instance<Animal> animalInstance = animals.select(literal); return animalInstance.get(); } } 

And the client:

 public class Client { @Inject private AnimalFactory animalFactory; public void run(String animalName) { Animal animal = animalFactory.getAnimal(animalName); animal.eat("some food..."); } } 

I do not know that placing the animalMap card in this place is correct ...?

+5
source share
2 answers

Using Instance<T> in combination with qualifiers is the standard way in CDI to perform dynamic injection.

You need a qualifier with a binding argument, for example. @Species("ant") to distinguish your implementation classes.

 public class AnimalSelector { @Inject @Any private Instance<Animal> animals; public Animal selectAnimalBySpecies(String speciesName) { SpeciesLiteral qualifier = new SpeciesLiteral(speciesName); return animals.select(qualifier).get(); } } public class SpeciesLiteral extends AnnotationLiteral<Species> implements Species { private String name; public SpeciesLiteral(String name) { this.name = name; } @Override public String value() { return name; } } @Qualifier @Target({ TYPE, METHOD, PARAMETER, FIELD }) @Retention(RUNTIME) @Documented public @interface Species { String value() default ""; } 
+6
source

I don’t think it is possible to achieve such a dynamic injection with CDI, simply because dependencies are introduced when the container creates a managed bean. In other words, if you call run() on AnimalManager , then the Animal dependency would have already been introduced.

What you can do is introduce AnimalFactory , and each call simply lets you say AnimalFactory.createAnimal(animal); or use something like your approach to the map.

+1
source

Source: https://habr.com/ru/post/1235414/


All Articles