user2362840
user2362840

Reputation: 183

CDI Dynamic Bean Instances

Working with Wildfly / JBoss Weld / CDI 1.1.

Lets say you have a database. Can be anything, MySQL, MongoDB. Could be a REST service. From that database, you get a list of Animals.

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

You do not know what animals you will get from this service, but what you want to do is make them available for Instance injection.

Animal Class:

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

Injection Point:

@Inject @Any
public Instance<Animal> animals;

You can make a Producer method that makes AN animal, for instance with a qualifier to make a certain animal:

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

But how do you Produce ALL (known from data) animals so that you can iterate over them with instance?

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

I do want each Animal to get the benefits of Dependency Injection and other Weld/CDI goodies.

Upvotes: 2

Views: 1793

Answers (2)

Jan Piel
Jan Piel

Reputation: 540

As far as I understand the InjectionPoint concept, you can not use it with Instance in that way. Instance and InjectionPoint with producer-method is used to inject the

 Instance<Animal>

into the producer-method with all CDI-animals and the let the producer-method decide, which animal to return, depending on the InjectionPoint:

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

See also here https://www.javacodegeeks.com/2013/06/java-ee-cdi-programmatic-dependency-disambiguation-example-injection-point-inspection.html

Or to produce a configured Object, depending on Qualifier-Config-Inputs like here https://dzone.com/articles/cdi-di-p2

In your case you must tell CDI, how to find the producer-methods:

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

Then you can write the appropriate producer methods:

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

And then you can inject it:

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

But in this case you have to write a producer-method for each selection-case. I fear, that's not suitable.

To use the InjectionPoint, you can make the AnimalType NOT a Qualifier

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

Now you must use the 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");
}

But you cannot inject Instance into your bean because lack of the Qualifier AnimalType. So you have to produce a simple List like this:

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

So you can inject just one or all

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

Upvotes: 0

John Ament
John Ament

Reputation: 11723

It seems like you might be looking for Unmanaged

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

Its basically a dependent scoped bean, but you need to manually destroy it when done.

Upvotes: 1

Related Questions