SeamFramework.orgCommunity Documentation
Métodos produtores permitem superarmos certas limitações que surgem quando o gerenciador do Web Bean, em vez da aplicação, é responsável por instanciar objetos. Eles são também a forma mais fácil de integrar os objetos que não são Web Beans ao ambiente Web Beans. (Veremos uma segunda abordagem em Capítulo 12, Definindo Web Beans utilizando XML.)
De acordo com a especificação:
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.
.O escopo padrão dos métodos produtores é @Dependent
, e, por isso ,serão chamados toda vez que o gerenciador do Web Bean injetar esse atributo ou qualquer outro atributo que resolve para o mesmo método produtor. Assim, pode haver várias instâncias do objeto PaymentStrategy
para cada sessão do usuário.
Para mudar esse comportamento, nós podemos adicionar a anotação @SessionScoped
ao método.
@Produces @Preferred @SessionScoped
public PaymentStrategy getPaymentStrategy() {
...
}
Agora, quando o método produtor é chamado, o PaymentStrategy
retornado será associado ao contexto sessão. O método produtor não será invocado novamente na mesma sessão.
Existe um problema potencial com o código acima. As implementações de CreditCardPaymentStrategy
são instanciadas utilizando o operador de Java new
. Objetos instanciados diretamente pela aplicação não usufruem da injeção de dependência e não possuem interceptadores.
Se não é isso o que queremos, podemos utilizar a injeção de dependência no método produtor para obter instâncias do 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;
}
}
Espere, o que se CreditCardPaymentStrategy
é um Web Bean de escopo de requisição? Então o método produtor tem o efeito de "promover" a instância atual no escopo de requisição para o escopo de sessão. Isso certamente é um erro! O objeto no escopo de requisição será destruído pelo gerenciador do Web Bean antes de terminar a sessão, mas a referência ao objeto será deixada "presa" no escopo sessão. Esse erro não será detectado pelo gerenciador do Web Bean. Por isso, tome cuidado quando retornar instâncias de Web Bean em métodos produtores!
Existem pelo menos três maneiras de corrigirmos esse erro. Podemos alterar o escopo da implementação do CreditCardPaymentStrategy
, mas isso poderia afetar outros clientes desse Web Bean. A mehor opção seria alterar o escopo do médoto produtor para @Dependent
ou @RequestScoped
.
Mas, uma solução mais comum é utilizar a anotação especial de binding @New
Considere o seguinte método produtor:
@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;
}
}
Assim a nova instância dependente de CreditCardPaymentStrategy
será criada, passada para o método produtor, retornada pelo método produtor e, finalmente, associada ao contexto de sessão. O objeto dependente não será destruído até que o objeto Preferences
seja destruído, no término da sessão.