méthode générique CDI producteur ne fonctionne pas comme prévu
Question
J'ai une méthode de production CDI qui - selon certaines conditions ne sont pas pertinentes à cet exemple - crée des objets de différents types:
public class TestProducer {
@Produces @TestQualifier
public Object create(InjectionPoint ip) {
if(something) {
return "a String";
} else {
return Integer.valueOf(42);
}
}
mais lorsque vous utilisez ce producteur, je reçois toujours une erreur dans la situation followin:
@Named("test")
public class TestComponent {
...
@Inject public void setA(@TestQualifier String stringValue) {
...
@Inject public void setB(@TestQualifier Integer integerValue) {
Il ne fonctionne que lorsque la méthode create du producteur a le type prévu dans la signature de la méthode:
public class TestProducer {
@Produces @SpringBean
public String create(InjectionPoint ip) {
Maintenant, la chaîne get injecté correctement, mais je n'ai aucun moyen de générer également un nombre entier de la méthode de production. Mais c'est exactement ce que je veux éviter, puisque le producteur lui-même doit être complètement générique.
Ai-je fait quelque chose de mal ou est-il pas moyen d'obtenir le comportement que je veux?
La solution
Tous les documents CDI, il est clair que le CDI fait typesafe injection de dépendance - et il est une propriété élevé de CDI. À mon humble avis, ce que vous essayez de faire est tout ce que CDI essaie d'éviter. Vous voulez que le récipient moulé Object
à chaque type et CDI ne fonctionne pas de cette façon.
Les injections de points stringValue
et integerValue
ne peut recevoir un haricot qui a java.lang.String
et java.lang.Integer
dans sa liste types de haricots respectivement. java.lang.Object
ne satisfait pas à ce critère.
J'ai deux suggestions. Tout d'abord, puisque vous avez deux ou plusieurs points d'injection de différents types, créez deux ou plusieurs méthodes pour la production que les 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
}
}
// ...
Il fonctionne si la condition de something
est juste pour vérifier le type de point d'injection (ce que je fais le pari est le cas).
Cependant, si la condition de something
ne décide du type en utilisant d'autres critères que le type du point d'injection, je suggestion de faire vous-même « sale boulot »: Injecter la valeur retournée dans un point d'injection typé Object
et fait la coulée manuellement:
@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;
Le point principal est que, contrairement à d'autres cadres de DI, le CDI ne fonctionne pas contre le système de type Java - au contraire, il utilise beaucoup il. Ne pas essayer de lutter contre elle, mais utiliser cet aspect du CDI en votre faveur:)
Autres conseils
Un producteur pour Object
est étrange de toute façon. Je ne sais pas si cela est interdit par la spécification, ou il est un bug, mais je pense que vous pouvez faire une solution intelligente:
public class ValueHolder<T> {
private T value;
public T getValue() {
return value;
}
}
Et puis Injecter ValueHolder<String>
et ValueHolder<Integer>
Il est possible de créer des objets génériques avec CDI produit comme ça:
// 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();
}
}
Travaux sur la soudure 2.2.0. Je pense que les travaux sur certaines versions précédentes ainsi.
Vos méthodes de initialiseur vont chercher un bean géré avec des types d'API chaîne et entier, mais votre grain de méthode de production a seul type API (en cas de la méthode de production, type de retour) Objet.
Vous pouvez donc utiliser seul objet dans votre initialiseur méthode injectée champs et discriminante entre les types int le corps du récepteur, ou tout simplement les envelopper et la méthode de production dans un type réel qui peut renvoyer des chaînes ou Int (mais je » d éviter les génériques)