SeamFramework.orgCommunity Documentation

Capitolo 6. Metodi produttori

6.1. Scope di un metodo produttore
6.2. Iniezione nei metodi produttori
6.3. Uso di @New con i metodi produttori

I metodi produttori consentono di superare alcune limitazioni che sorgono quando il manager Web Bean è responsabile dell'istanziamento degli oggetti al posto dell'applicazione. Questi metodi sono anche il modo migliore per integrare gli oggetti che non sono Web Beans dentro l'ambiente Web Beans. (Si incontrerà un secondo approccio in Capitolo 12, Definire i Web Beans tramite XML.)

Secondo la specifica:

Un metodo produttore Web Bean agisce come sorgente di oggetti da iniettare, dove:

  • gli oggetti da iniettare non è richiesto siano istanze di Web Beans,

  • il tipo concreto di oggetti da iniettare può variare a runtime o

  • gli oggetti richiedono alcune inizializzazini personalizzate che non vengono eseguite dal costruttore Web Bean

Per esempio un metodo produttore consente di:

In particolare, i metodi produttori consentono di usare un polimorfismo a runtime con i Web Beans. Come visto, i tipi di deploy sono soluzioni al problema del polimorfismo durante la fase di deploy. Ma una volta che il sistema viene deployato, l'implementazione del Web Bean viene fissata. Un metodo produttore non ha questi limiti:

@SessionScoped

public class Preferences {
    
    private PaymentStrategyType paymentStrategy;
    
    ...
    
    @Produces @Preferred 
    public PaymentStrategy getPaymentStrategy() {
        switch (paymentStrategy) {
            case CREDIT_CARD: return new CreditCardPaymentStrategy();
            case CHEQUE: return new ChequePaymentStrategy();
            case PAYPAL: return new PayPalPaymentStrategy();
            default: return null;
        } 
    }
    
}

Si consideri un punto di iniezione:

@Preferred PaymentStrategy paymentStrat;

Questo punto di iniezione ha lo stesso tipo e annotazioni di binding del metodo produttore, e quindi risolve i metodi produttori usando le regole di iniezione dei Web Beans. Il metodo produttore verrà chiamato dal manager Web Bean per ottenere un'istanza per servire questo punto di iniezione.

Lo scope dei metodi produttori è di default impostato a @Dependent, e quindi verrà chiamato ogni volta che il manager Web Bean inietta questo campo o qualsiasi altro campi che risolve lo stesso metodo produttore. Quindi ci potrebbero essere istanze multiple dell'oggetto PaymentStrategy per ogni sessione utente.

Per cambiare questo comportamento si può aggiungere un'annotazione @SessionScoped al metodo.

@Produces @Preferred @SessionScoped

public PaymentStrategy getPaymentStrategy() {
    ...
}

Ora, quando il metodo produttore viene chiamato, il PaymentStrategy restituito verrà associato al contesto di sessione. Il metodo produttore non verrà più chiamato nella stessa sessione.

C'è un potenziale problema con il codice visto sopra. Le implementazioni di CreditCardPaymentStrategy vengono istanziate usando l'operatore Java new. Gli oggetti istanziati direttamente dall'applicazione non possono sfruttare la dependency injection e non hanno interceptor.

Se questo non è ciò che si vuole, è possibile usare la dependency injection nel metodo produttore per ottenere istanze Web Bean:

@Produces @Preferred @SessionScoped

public PaymentStrategy getPaymentStrategy(CreditCardPaymentStrategy ccps,
                                          ChequePaymentStrategy cps,
                                          PayPalPaymentStrategy ppps) {
    switch (paymentStrategy) {
        case CREDIT_CARD: return ccps;
        case CHEQUE: return cps;
        case PAYPAL: return ppps;
        default: return null;
    } 
}

Ma cosa succede se CreditCardPaymentStrategy è un Web Bean con scope di tipo richiesta? Il metodo produttore ha l'effetto di "promuovere" l'istanza corrente con scope di tipo richiesta a scope di tipo sessione. Questo è quasi certamente un bug! L'oggetto con scope richiesta verrà distrutto dal manager Web Bean prima che la sessione termini. Quest'errore non verrà rilevato dal manager Web Bean, quindi si faccia attenzione quando si restituiscono istanze Web Bean dai metodi produttori!

Ci sono almeno 3 modi per correggere questo bug. Si può cambiare lo scope dell'implementazione di CreditCardPaymentStrategy, ma questo non influenzerebbe gli altri client di questo Web Bean. Un'opzione migliore sarebbe quella di cambiare lo scope del metodo produttore a @Dependent o @RequestScoped.

Ma una soluzione più comune è quella di usare la speciale annotazione di binding @New.

Si consideri il seguente metodo produttore:

@Produces @Preferred @SessionScoped

public PaymentStrategy getPaymentStrategy(@New CreditCardPaymentStrategy ccps,
                                          @New ChequePaymentStrategy cps,
                                          @New PayPalPaymentStrategy ppps) {
    switch (paymentStrategy) {
        case CREDIT_CARD: return ccps;
        case CHEQUE: return cps;
        case PAYPAL: return ppps;
        default: return null;
    } 
}

Quindi verrebbe creata una nuova istanza dipendente di CreditCardPaymentStrategy, passata al metodo produttore, ritornata al metodo produttore ed infine associata al contesto di sessione. L'oggetto dipendente non verrebbe distrutto finché l'oggetto Preferences non viene distrutto, cioè a fine sessione.