CDI producer generic method not working properly

I have a method for creating a CDI that, depending on some conditions that are 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 manufacturer I always get an error in the following situation:

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

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

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

Now the string is entered correctly, but I have no way to generate an integer from the producer method. But this is exactly what I want to avoid, since the manufacturer itself must be completely common.

Am I doing something wrong or is there no way to achieve the desired behavior?

+4
source share
4 answers

All CDI documentation makes it clear that CDI does impose dependency injection - and that’s a high CDI value. IMHO, what you are trying to do is exactly what CDI is trying to avoid. You want the container to use Object for each type, and CDI does not work that way.

The injection points stringValue and integerValue can only accept a bean that has java.lang.String and java.lang.Integer in its list of bean types, respectively. java.lang.Object does not meet this criterion.

I have two suggestions. First, since you have two or more injection points of different types, create two or more creation methods for these 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 condition of something is to simply check the type of injection point (what I'm betting on).

However, if the something condition determines the type using different criteria than the type of the injection point, I suggest that you do the dirty work yourself: enter the return value in the Object -typed injection and make a manual throw:

 @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 thing is that, unlike some other DI structures, CDI does not work against a system like Java - on the contrary, it uses it heavily. Do not try to deal with this, but use this aspect of CDI to your advantage :)

+4
source

In any case, the manufacturer for Object strange. I'm not sure if this is forbidden by the spec, or it is a mistake, but I think you can do some smart workarounds:

 public class ValueHolder<T> { private T value; public T getValue() { return value; } } 

And then enter ValueHolder<String> and ValueHolder<Integer>

+4
source

It is possible to create its common objects with CDI:

  // the wrapper class public class Wrapper<T> { public final T bean; public Wrapper(T bean){ this.bean = bean; } } // the producer inside some class @Produces public <T> Wrapper<T> create(InjectionPoint p){ // with parameter 'p', it is possible retrieve the class type of <T>, at runtime } // the bean example 1 public class BeanA { public void doFoo(){ // ... } } // the bean example 2 public class BeanB { public void doBar(){ // ... } } // the class that uses the produced beans public class SomeBean{ //// There on producer method, do you can retrieve the Class object of BeanA and BeanB, from type parameters of Wrapper. @Inject private Wrapper<BeanA> containerA; @Inject private Wrapper<BeanB> containerB; public void doSomeThing(){ containerA.doFoo(); containerB.doBar(); } } 

Welding work 2.2.0. I think this works in some previous versions.

+2
source

Your initialization methods will look for a managed bean with the String and Integer API types, but your bean producer method has only an API type (in the case of a producer, a return type) Object.

That way, you can only use Object in your initialization methods, and then distinguish between int types in the receiver body, or just wrap them and the producer method in an actual type that can return strings or Int (but I'd avoid generics)

+1
source

All Articles