Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic CDI producer method not working as expected

I have a CDI producer method which - depending on some conditions not relevant to this example - creates objects of different types:

public class TestProducer {

  @Produces @TestQualifier
  public Object create(InjectionPoint ip) {
    if(something) {
      return "a String";
    } else {
      return Integer.valueOf(42);
    }
  }

but when using this producer, I always get an error in the followin situation:

@Named("test")
public class TestComponent {
   ...
   @Inject public void setA(@TestQualifier String stringValue) {
   ...
   @Inject public void setB(@TestQualifier Integer integerValue) {

It only works when the create method of the producer has the expected type in the method signature:

public class TestProducer {

  @Produces @SpringBean
  public String create(InjectionPoint ip) {

Now the String get's injected correctly, but I have no way to also generate an integer from the producer method. But this is exactly what I want to avoid, since the producer itself should be completely generic.

Am I doing something wrong or is there no way to achieve the behaviour I want?

like image 545
Christian Seifert Avatar asked Dec 17 '22 19:12

Christian Seifert


2 Answers

All CDI documentation makes it clear that CDI does typesafe dependency injection - and it is an exalted property of CDI. IMHO, what you are trying to do is just what CDI tries to avoid. You want the container to cast Object to each type and CDI does not work that way.

The injections points stringValue and integerValue can only receive a bean which has java.lang.String and java.lang.Integer in its list of bean types respectively. java.lang.Object does not satisfy this criterion.

I have two suggestions. First, since you have two or more injection points of different types, create two or more producer methods for that types:

public class TestProducer {

  @Produces @TestQualifier
  public String createString(InjectionPoint ip) {
    if(something) {
      return "a String";
    } else {
      // Some other value
    }
  }

  @Produces @TestQualifier
  public int createInt(InjectionPoint ip) {
    if(something) {
      return 42;
    } else {
      // Some other value
    }
  }
// ...

It works if the something condition is just to check the type of the injection point (what I am betting is the case).

However, if the something condition does decide the type using other criteria than the type of the injection point, I'd suggestion to do the "dirty job" yourself: inject the returned value in an Object-typed injection point and does the cast manually:

@Named("test")
public class TestComponent {
   ...
   @Inject public void setA(@TestQualifier Object value) {
       String stringValue = (String) value;

   ...
   @Inject public void setB(@TestQualifier Object value) {
       int intValue = (Integer) value;

The main point is that, unlike some other DI frameworks, CDI does not work against the Java type system - on the contrary, it heavily uses it. Do not try to fight against it but use this aspect of CDI in your favor :)

like image 60
brandizzi Avatar answered Dec 25 '22 09:12

brandizzi


A producer for Object is strange anyway. I'm not sure if this is forbidden by the spec, or it's a bug, but I think you can make some clever workaround:

public class ValueHolder<T> {
    private T value;

    public T getValue() {
        return value;
    }
}

And then inject a ValueHolder<String> and ValueHolder<Integer>

like image 26
Bozho Avatar answered Dec 25 '22 08:12

Bozho