通过CDI动态注入实例

时间:2015-11-07 12:58:43

标签: java java-ee cdi

在具有CDI的Java EE平台中,能够注入POJO类的实例。 以非常简单的方式,我们需要使用@Inject注释来注入某个接口的默认实例。我们也可以使用限定符为我们的领域注入具体的类。 但这些解决方案相当静态。

我需要一些更具动态性的注射模型。

让我来介绍一下我的问题: 假设我们有接口Animal和三个实现该接口的类:Ant,Dog,Elephant。 我想动态地注入这三个类中的一个的实例,它依赖于一些变量,如字符串(动物名称)。 在Java SE中我会这样做:

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();

所以我需要这样的东西:

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

在所有实现Animal接口的类中,我需要使用带有@EJB注释的变量。

在Java EE中使用它的最佳和最正确的方法是什么?

编辑:
好的,基于Svetlin Zarev和hwellmann的回应(谢谢!)我创造了这个:

一开始我们将创建动物模型:

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface AnimalModel {

   Type value();

   enum Type { ANT, DOG, ELEPHANT }

}

让我们创建动物:

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

接下来,具体课程:

@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) {
      ...
   }

}

接下来,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;
   }
}

主要成分是动物工厂:

@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();
   }
}

客户:

public class Client {

   @Inject
   private AnimalFactory animalFactory;

   public void run(String animalName) {
      Animal animal = animalFactory.getAnimal(animalName);
      animal.eat("some food...");
   }

}

我不知道将地图动物地图放在那个地方是对的......?

2 个答案:

答案 0 :(得分:7)

Instance<T>与限定符结合使用是CDI执行动态注入的标准方法。

您需要一个带有绑定参数的限定符,例如@Species("ant")区分您的实施类。

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 "";
}

答案 1 :(得分:1)

我认为用CDI实现这种动态注入是不可能的,因为当容器实例化托管bean时会注入依赖关系。换句话说,当您在run()上致电AnimalManager时,Animal依赖关系已经被注入。

你可以做的是注入AnimalFactory并在每次通话时只需说出AnimalFactory.createAnimal(animal);或使用类似地图方式的内容。