SeamFramework.orgCommunity Documentation
Producer method 讓我們在當 Web Bean 管理員(而不是應用程式)負責舉例說明(instantiating)物件時能夠解決一些特定發生的問題。它們同時也是將非 Web Bean 物件整合入 Web Bean 環境中最簡單的方式。(我們將在 章 12, 使用 XML 來定義 Web Bean 中提及第二個方式。)
根據規格:
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.
.producer method 的預設 scope 為 @Dependent
,所以每當 Web Bean 管理員注入此欄位或是其它任何會解析為相同 producer method 的欄位時,@Dependent
就會被調用。因此,各個用戶 session 都可能會有多個 PaymentStrategy
物件的 instance 出現。
若要更改此特性,我們可將 @SessionScoped
這個標記附加至 method。
@Produces @Preferred @SessionScoped
public PaymentStrategy getPaymentStrategy() {
...
}
現在,當 producer method 被調用時,回傳的 PaymentStrategy
將會被綁定至 session context。Producer method 將不會在相同的 session 中再次被調用。
上述程式碼有個潛在的問題。CreditCardPaymentStrategy
的實做是透過使用 Java 的 new
operator 來例示(instantiate)的。被應用程式直接例示的物件無法有效利用依賴注入(dependency injection)並且沒有攔截器。
若這不是我們所想要的,我們可使用依賴注入至 producer method 之中來取得 Web Bean 的 instance:
@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;
}
}
請稍等,若 CreditCardPaymentStrategy
是個請求 scope 的 Web Bean 的話要怎麼辦?若是如此那麼 producer method 便有將現有的 request scope 的 instance「推入」session scope 中的效應。這幾乎能被確定是個 bug!request scope 物件會在 session 結束前被 Web Bean 管理員毀掉,不過該物件的參照會被「保留」在 session scope 中。這項錯誤將不會被 Web Bean 管理員偵測到,所以當由 producer method 回傳 Web Bean 的 instance 時請格外小心!
我們有三種可修正這項錯誤的方法。我們可更改 CreditCardPaymentStrategy
實做的 scope,不過這將會影響該 Web Bean 的其它客戶端。較好的方式就是將 producer method 的 scope 更改為 @Dependent
或 @RequestScoped
。
不過較普遍的解決方式就是使用特殊的 @New
綁定標記。
考慮下列 producer method:
@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;
}
}
接著有個新的 CreditCardPaymentStrategy
的 dependent instance 將會被建立、傳送至 producer method、被 producer method 回傳然後最後被綁定至 session context。在 Preferences
物件於 session 結束時被毀掉之前,dependent 物件不會被毀掉。