2014-04-10 6 views
8

Sto usando il codice come di seguito:CDI Dipendenza ambigua con @Produces - perché?

public Configuration { 

    private boolean isBatmanCar = someMethod(...); 

    @Produces 
    public Car getCar(@New Car car) { 
     if(isBatmanCar) { 
      car.setName("BatmanCar"); 
     } 
     return car; 
    } 
} 

public Car { 
    private String name = "NormalCar"; 

    public void setName(String name) { 
     this.name = name; 
    } 
} 

public Demo { 
    @Inject 
    Car car; 

    // rest of code 
} 

Quando schiero un'applicazione per Glassfish (Java EE 6 btw) ottengo

AmbiguousResolutionException: WELD-001318 Cannot resolve an ambiguous dependency between (...) Car with qualifiers [@Any @Default] (...) Producer Method [Car] with qualifiers [@Any @Default]

So che quando aggiungo @Alternative alla classe Car funzionerà, ma mi chiedo se questo è il modo corretto per farlo, e perché devo farlo?

Puoi dirmi qual è l'uso corretto di @Produces in tal caso?

sto usando Java EE 6, CDI 1.0, EJB 3.1, 3.2 Glassfish

risposta

12

L'errore deriva dal fatto che si dispone di 2 fagioli di tipo Car, uno è la classe, l'altro è il produttore. Hai 2 soluzioni ovvie per risolvere l'ambiguità:

Innanzitutto, hai inserito la logica dietro il campo isBatmanCar nella classe originale (in un costruttore o in un metodo @PostConstruct per esempio) e rimuovi il produttore. Ciò avrebbe lasciato solo un fagiolo Car.

O se si vuole veramente avere 2 di fagioli o non può evitarlo è necessario creare un qualificatore per il bean prodotto:

@Target({ TYPE, METHOD, PARAMETER, FIELD }) 
@Retention(RUNTIME) 
@Documented 
@Qualifier 
public @interface BatmanChecked { 
} 

e usarlo su produttore,

@Produces 
@BatmanChecked 
public Car getCar(Car car) {...} 

a essere in grado di iniettare il tipo di auto

@Inject 
Car stdCar; 

@Inject 
@BatmanChecked 
Car batCheckedCar; 

Qualificatore è l'opzione naturale per risolvere l'iniezione ambigua. L'utilizzo di @Alternative funziona anche ma è più un trucco che una buona pratica.

Ultimo commento: @New non è necessario qui, poiché il tuo bean Car non ha scope (quindi è @Dependent con scope). @Nuovo è utile solo quando un produttore inserisce un bean con un ambito che non è @Dependent. Detto questo, questo codice non è molto utile se la tua classe Car è nello scope @Dependent.

+0

Qual è la buona pratica per l'utilizzo di @Alternative? – dmydlarz

+0

@Alternative consente di proporre versioni diverse per lo stesso bean (tipo) e attivarle tramite la configurazione in un file di configurazione. Può essere utile avere un ben diverso in dev/test e in produzione, ad esempio, cambiandolo solo nel file beans.xml. –

5

Un'altra possibilità sarebbe quella di creare il costruttore non predefinito nella classe Car in questo modo:

public Car { 
    private String name = "NormalCar"; 

    public Car(String name) { 
     this.name = name; 
    } 
    ... 
} 

rimuovendo costruttore di default, la classe auto non può più essere utilizzato per creare le istanze utilizzate per iniezione.

E cambiare il metodo produttore al

@Produces 
public Car getCar() { 
    if(isBatmanCar) { 
     return new Car("BatmanCar"); 
    } 
    return new Car("NormalCar"); 
} 

poi metodo produttore sarebbe l'unico modo per creare la sua auto.

Questo modo può essere utilizzato, quando sai che avresti sempre bisogno di un'istanza personalizzata e non hai bisogno del costruttore predefinito. Ma di solito la soluzione di Antoine è più utile.

8

Utilizzare @Alternative funziona ma deve essere utilizzato solo se si desidera essere in grado di attivarlo tramite bean.xml.

La soppressione del costruttore predefinito del bean funziona ma non sarà possibile utilizzare il bean in un ambito diverso da @RequestScoped.

L'utilizzo del proprio qualificatore funziona ma non è molto utile se si dispone di una sola implementazione e si desidera solo poter creare un'istanza del bean con un produttore anziché con il suo costruttore.

Il modo più semplice è quello di annotare il bean @Any:

@Any 
public class Car { 
} 
... 
@Produces 
public Car getCar() { 
    return new Car(); 
} 
... 
@Inject 
Car car; 

cose che dovete tenere a mente:

  • Tutti i fagioli e produttori sono sempre implicitamente qualificato @Any
  • Fagioli e produttori senza qualificazioni esplicite sono implicitamente qualificati @Default
  • I bean e i produttori con qualificazioni esplicite non sono più implicitamente qualificati ied @Default
  • punti di iniezione senza qualificazioni espliciti sono @Default implicitamente qualificato, ma non @Any

Per quanto riguarda tutto questo, lo stesso codice è come sopra esplicitamente qualificato come questo:

@Any 
public class Car { 
} 
... 
@Produces 
@Any 
@Default 
public Car getCar() { 
    return new Car(); 
} 
... 
@Inject 
@Default 
Car car; 

E ' diventa più ovvio che il costruttore di default del bean non è una valida possibilità per il punto di iniezione e il produttore è una possibilità valida.