metodo produttore CDI generico non funziona come previsto
Domanda
Ho un metodo produttore CDI, che - a seconda di alcune condizioni che non interessano a questo esempio - crea oggetti di tipo diverso:
public class TestProducer {
@Produces @TestQualifier
public Object create(InjectionPoint ip) {
if(something) {
return "a String";
} else {
return Integer.valueOf(42);
}
}
, ma quando si utilizza questo produttore, ho sempre arrivare un errore nella situazione followin:
@Named("test")
public class TestComponent {
...
@Inject public void setA(@TestQualifier String stringValue) {
...
@Inject public void setB(@TestQualifier Integer integerValue) {
Funziona solo quando il metodo del produttore creare ha il tipo previsto nella firma del metodo:
public class TestProducer {
@Produces @SpringBean
public String create(InjectionPoint ip) {
Ora la stringa ottenere iniettato correttamente, ma non ho modo di generare anche un numero intero da metodo produttore. Ma questo è esattamente quello che voglio evitare, dal momento che il produttore stesso dovrebbe essere del tutto generico.
sto facendo qualcosa di sbagliato o non c'è un modo per ottenere il comportamento desiderato?
Soluzione
documentazione Tutto CDI chiarisce che CDI typesafe iniezione di dipendenza - ed è una proprietà esaltato di CDI. IMHO, ciò che si sta cercando di fare è proprio quello CDI cerca di evitare. Si desidera che il contenitore per getto Object
di ogni tipo e CDI non funziona in questo modo.
I punti iniezioni stringValue
e integerValue
può ricevere solo un fagiolo che ha java.lang.String
e java.lang.Integer
nella sua lista di tipi di fagioli rispettivamente. java.lang.Object
non soddisfa tale criterio.
Ho due suggerimenti. In primo luogo, dal momento che si dispone di due o più punti di iniezione di tipo diverso, creare due o più produttori metodi per che i tipi:
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
}
}
// ...
Funziona se la condizione something
è solo quello di verificare il tipo di punto di iniezione (quello che sto scommesse è il caso).
Tuttavia, se la condizione something
fa decidere il tipo utilizzando altri criteri rispetto al tipo del punto di iniezione, mi piacerebbe suggerimento per fare il "lavoro sporco" voi stessi: Iniettare il valore restituito in un punto di iniezione Object
tipizzato e lo fa il cast manualmente:
@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;
Il punto principale è che, a differenza di alcuni altri quadri DI, CDI non funziona contro il sistema di tipo Java - al contrario, utilizza pesantemente esso. Non cercare di lottare contro di esso, ma utilizzare questo aspetto della CDI a tuo favore:)
Altri suggerimenti
Un produttore per Object
è strano comunque. Non sono sicuro se questo è vietato dalle specifiche, o si tratta di un bug, ma credo che si può fare qualche soluzione intelligente:
public class ValueHolder<T> {
private T value;
public T getValue() {
return value;
}
}
E poi iniettare un ValueHolder<String>
e ValueHolder<Integer>
I suoi oggetti possibile creare generici con CDI produce così:
// 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();
}
}
Opere su di saldatura 2.2.0. Penso che funziona su alcune versioni precedenti pure.
I tuoi metodi di inizializzazione andrà a cercare un bean gestito con i tipi di API String e Integer, ma il metodo di fagioli produttore ha solo tipo di API (in caso di metodo produttore, tipo di ritorno) oggetto.
Si può quindi utilizzare solo oggetto nel initializer metodo campi iniettato e quindi discriminare tra i tipi int il corpo del ricevente, o semplicemente li e il metodo produttore in un tipo effettivo che può restituire stringhe o Int (ma io avvolgere' d evitare i farmaci generici)