2012-05-23 12 views
14

È possibile utilizzare CDI per inserire parametri nelle chiamate di metodo? Il comportamento previsto sarebbe simile all'iniezione sul campo. Il produttore preferito viene consultato e il prodotto viene utilizzato.Come utilizzare CDI per l'iniezione dei parametri del metodo?

Quello che vorrei fare è questo:

public void foo(@Inject Bar bar){ 
    //do stuff 
} 

o del presente (con sytax meno confusione):

public void foo(){ 
    @Inject 
    Bar bar; 
    //do stuff 
} 

Questa sintassi è illegale in entrambi i casi. C'è un'alternativa? Se no, sarebbe una cattiva idea per qualche motivo se fosse possibile?

Grazie

EDIT - io ho fatto le mie esigenze non abbastanza chiari - mi piacerebbe essere in grado di chiamare il metodo direttamente, lasciando l'inizializzazione della bar variabile al contenitore. La risposta di Jörn Horstmann e Perception suggerisce che non è possibile.

risposta

16

I punti di iniezione vengono elaborati per un bean quando viene istanziato dal contenitore, il che limita il numero di casi di utilizzo per l'iniezione a livello di metodo. La versione attuale della specifica riconosce i seguenti tipi di metodo di iniezione:

metodo Initializer iniezione

public class MyBean { 
    private Processor processor; 

    @Inject 
    public void setProcessor(final Processor processor) { 
     this.processor = processor; 
    } 
} 

Quando un'istanza di MyBean viene iniettato, l'istanza processore sarà anche iniettato, tramite è metodo setter . Metodi

Evento Observer

public class MyEventHandler { 
    public void processSomeEvent(@Observes final SomeEvent event) { 
    } 
} 

L'istanza evento viene iniettato nel metodo di gestione eventi direttamente (anche se non con l'annotazione @Inject)

metodi produttori

public class ProcessorFactory { 
    @Produces public Processor getProcessor(@Inject final Gateway gateway) { 
     // ... 
    } 
} 

I parametri dei metodi di produzione vengono iniettati automaticamente ed.

+0

Grazie, Percezione. La prima frase è stata sufficiente a rovinare il mio sogno :) "quando viene istanziato". Quello che avevo in mente dovrebbe funzionare come un metodo produttore senza esserlo. Immagino che il mio caso d'uso non fosse inteso dal gruppo di esperti. – kostja

+0

Sì, sfortunatamente le specifiche non impongono che le chiamate ai metodi facciano parte della gestione del ciclo di vita dei bean. Quindi, chiamare direttamente il metodo * non * invocherà l'iniezione (è simile a chiamare 'new' direttamente sull'oggetto). Non sarei sorpreso se l'iniezione di metodo lo rendesse nella prossima versione della specifica. – Perception

5

Quella caratteristica di CDI è definita "metodo di inizializzazione". La sintassi differisce dal codice in quanto l'intero metodo è annotato con @Inject, i parametri del metodo possono essere ulteriormente annotati dai qualificatori per selezionare un bean specifico. La sezione 3.9 di JSR 299 mostra il seguente esempio, con @Selected come qualificatore che può essere omesso se esiste una sola implementazione di bean.

@Inject 
void setProduct(@Selected Product product) { 
    this.product = product; 
} 

Si noti che

L'applicazione può chiamare direttamente metodi di inizializzazione, ma poi non verranno passati parametri al metodo dal contenitore.

+0

Grazie, Jörn, ho letto questa specifica. Il caso nella tua nota è esattamente quello che mi piacerebbe fare: chiamare il metodo direttamente E fare in modo che il contenitore fornisca un'istanza di bean. C'è un'altra possibilità in CDI per questo? – kostja

1

È possibile utilizzare l'API BeanManager nel metodo per ottenere riferimenti contestuali, o a seconda del vostro obiettivo finale si potrebbe iniettare un

Instance<Bar> 

al di fuori del metodo e utilizzarlo nel metodo.

+0

Grazie, covener. Bello sapere che può essere fatto dopo tutto, anche se "manualmente". Suppongo che la maggior parte dei casi non giustifichi il potenziale WTF aggiunto di un lokup esplicito, ma terrò presente questa possibilità. – kostja

7

Se ciò che VERAMENTE si desidera non è qualcosa come parametro del metodo (che dovrebbe essere fornito dal chiamante), ma un'istanza correttamente inizializzata di un bean CDI ogni volta che viene chiamato il metodo, e completamente costruito e iniettato , quindi controllare

javax.inject.Provider<T>

Fondamentalmente, prima iniettare un provider alla classe

@Inject Provider<YourBean> yourBeanProvider; 

poi, nel metodo, ottenere una nuova istanza

YourBean bean = yourBeanProvider.get(); 

Spero che questo aiuti :)

+1

Cool, grazie. Il bean non deve essere un parametro di metodo. Avevo solo bisogno di un'istanza di bean correttamente inizializzata per ogni chiamata di metodo senza che il chiamante lo fornisse. Quindi la tua risposta lo inchioda :) – kostja

4

Questa domanda è venuto quando ho inizialmente fatto una ricerca su questo tema, e ho imparato da che con il rilascio di CDI 1.1 (incluso nel JavaEE 7 spec), ora c'è un modo per fare effettivamente ciò che l'OP voleva, parzialmente. Ancora non è possibile fare

public void foo(@Inject Bar bar){ 
    //do stuff 
} 

ma si può "iniettare" una variabile locale, anche se non si utilizza @Inject ma piuttosto a livello di codice cercare l'istanza iniettato in questo modo:

public void foo() { 
    Instance<Bar> instance = CDI.current().select(Bar.class); 
    Bar bar = instance.get(); 
    CDI.current().destroy(instance); 
    // do stuff with bar here 
} 

Si noti che il metodo select() opzionalmente accetta qualsiasi annotazione del qualificatore che potrebbe essere necessario fornire. Buona fortuna per ottenere istanze di java.lang.annotation.Annotation. Potrebbe essere più facile scorrere il tuo Instance<Bar> per trovare quello che desideri.

Mi è stato detto che è necessario distruggere lo Instance<Bar> come ho fatto sopra, e posso verificare per esperienza che il codice sopra funziona; tuttavia, non posso giurare che è necessario per distruggerlo.

+0

Funziona bene ma potrebbe creare problemi nei test unitari. –