CDI 动态 Bean 实例

Posted

技术标签:

【中文标题】CDI 动态 Bean 实例【英文标题】:CDI Dynamic Bean Instances 【发布时间】:2016-08-05 06:18:17 【问题描述】:

使用 Wildfly/JBoss Weld/CDI 1.1。

假设您有一个数据库。可以是任何东西,mysql、MongoDB。可能是 REST 服务。从该数据库中,您可以获得动物列表。

[
  "Cat",
  "Dog",
  "Giraffe",
  "Tiger",
  "Chicken"
]

你不知道你会从这项服务中得到什么动物,但你想做的是让它们可用于实例注入。

动物类:

public class Animal 
  private final String type;
  public String getType() 
    return type;
  
  public Animal(String aType) 
    type = aType;
  

注入点:

@Inject @Any
public Instance<Animal> animals;

您可以创建一个制作动物的 Producer 方法,例如使用限定符来制作某种动物:

@Produces @AnimalType
public Animal makeAnimal(InjectionPoint ip) 
  // Get AnimalType qualifier and make a new Animal(typeString), 
  // ...
  return animal;

但是您如何生成所有(从数据中得知)动物,以便您可以通过实例对它们进行迭代?

for(Animal animal : animals) 
  // ...

我确实希望每只动物都能从依赖注入和其他 Weld/CDI 好东西中受益。

【问题讨论】:

【参考方案1】:

您可能正在寻找Unmanaged

Unmanaged<Animal> unmanagedAnimal = new Unmanaged<>(Animal.class);
UnmanagedInstance<Animal> animalInstance = unmanagedAnimal.newInstance();
Animal animal = animalInstance.produce().inject().postConstruct().get();

它基本上是一个依赖作用域的bean,但你需要在完成后手动销毁它。

【讨论】:

非托管它用于转换注入目标中的非 bean 类。这里需要能够在任何地方注射动物。非托管是不够的。唯一的解决办法就是把这些动物做成豆子……【参考方案2】:

据我了解 InjectionPoint 概念,您不能以这种方式将其与 Instance 一起使用。 Instance 和 InjectionPoint 与 producer-method 用于注入

 Instance<Animal>

与所有 CDI 动物一起进入生产者方法,然后让生产者方法决定返回哪个动物,具体取决于 InjectionPoint:

 public Animal make(@Any Instance<Animal> instance, InjectionPoint ip)

参见此处https://www.javacodegeeks.com/2013/06/java-ee-cdi-programmatic-dependency-disambiguation-example-injection-point-inspection.html

或者根据 Qualifier-Config-Inputs 生成一个配置的对象,像这里https://dzone.com/articles/cdi-di-p2

在您的情况下,您必须告诉 CDI,如何找到生产者方法:

  @Qualifier
  @Retention(RUNTIME)
  @Target(TYPE, METHOD, FIELD, PARAMETER)
  public @interface AnimalType 
      String value();   
  

然后就可以编写相应的生产者方法了:

@Produces
@AnimalType("Monkey")
public Animal makeAnimalApe() 
    return new Animal("Cheetah");



@Produces
@AnimalType("Mouse")
public Animal makeAnimalMouse() 
    return new Animal("Jerry");


@Produces
@AnimalType("Cat")
public Animal makeAnimalCat() 
    return new Animal("Tom");

然后你就可以注入它了:

@Inject
@Any
private Instance<Animal> anyAnimal;

@Inject
@AnimalType("Monkey")
private Animal monkey;

@PostConstruct
public void create()
    System.out.println(monkey.name);
    anyAnimal.forEach((a)->System.out.println(a.name));

但是在这种情况下,您必须为每个选择情况编写一个生产者方法。恐怕,那不合适。

要使用 InjectionPoint,您可以使 AnimalType 不是限定符

@Retention(RUNTIME)
@Target(TYPE, METHOD, FIELD, PARAMETER)
public @interface AnimalType 
    String value(); 

现在你必须使用 InjectionPoint:

@Produces
public Animal makeAnimalApe(InjectionPoint p) 
    AnimalType t = p.getAnnotated().getAnnotation(AnimalType.class);
    if (t != null) 
        String s = t.value();
        if ("Monkey".equals(s))
            return new Animal("Cheetah");
        else if ("Mouse".equals(s))
            return new Animal("Jerry");
        else if ("Cat".equals(s))
            return new Animal("Tom");
           
    throw new EJBException("Please annotate the animal injection point with AnimalType");

但是由于缺少限定符 AnimalType,您不能将 Instance 注入到您的 bean 中。所以你必须像这样生成一个简单的列表:

@Produces
public List<Animal> produceAll()
    List<Animal> all = new ArrayList<>();
    all.add(new Animal("Cheetah"));
    all.add(new Animal("Jerry"));
    all.add(new Animal("Tom"));
    return all;

所以你可以只注入一个或全部

@Inject
@AnimalType("Monkey")
private Animal monkey;
@Inject
private List<Animal> all;

【讨论】:

以上是关于CDI 动态 Bean 实例的主要内容,如果未能解决你的问题,请参考以下文章

获取 CDI 托管 bean 实例的规范方法:BeanManager#getReference() 与 Context#get()

启用 CDI 注入到由生产者方法创建的 bean

如何以编程方式将 Java CDI 托管 bean 注入(静态)方法中的局部变量

CDI FEATURES

CDI + JPA 多个 @OneToMany - LAZY/EAGER - LazyInitializationException 或无法实例化多个包

在 WEB-INF/lib 内的 JAR 中找不到 CDI 实例