SeamFramework.orgCommunity Documentation
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:
A Web Beans producer method acts as a source of objects to be injected, where:
the objects to be injected are not required to be instances of Web Beans,
the concrete type of the objects to be injected may vary at runtime or
the objects require some custom initialization that is not performed by the Web Bean constructor
For example, producer methods let us:
expose a JPA entity as a Web Bean,
expose any JDK class as a Web Bean,
define multiple Web Beans, with different scopes or initialization, for the same implementation class, or
vary the implementation of an API type at runtime.
In particular, producer methods let us use runtime polymorphism with Web Beans. As we've seen, deployment types are a powerful solution to the problem of deployment-time polymorphism. But once the system is deployed, the Web Bean implementation is fixed. A producer method has no such limitation:
@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;
}
}
}
Consider an injection point:
@Preferred PaymentStrategy paymentStrat;
This injection point has the same type and binding annotations as the producer method, so it resolves to the producer method using the usual Web Beans injection rules. The producer method will be called by the Web Bean manager to obtain an instance to service this injection point.
.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.