SeamFramework.orgCommunity Documentation
Producer method 讓我們在當 Web Bean 管理員(而不是應用程式)負責舉例說明(instantiating)物件時能夠解決一些特定發生的問題。它們同時也是將非 Web Bean 物件整合入 Web Bean 環境中最簡單的方式。(我們將在 章 12, 使用 XML 來定義 Web Bean 中提及第二個方式。)
根據規格:
Web Bean 的 producer method 的作用為要被注入之物件的來源:
要被注入的物件無須是 Web Bean 的 instance,
要被注入的物件的具體類型取決於 runtime,或是
該物件需要進行一些並非由 Web Bean 建構函式所實施的自訂初始化
比方說,producer method 能讓我們:
將 JPA entity 顯示為 Web Bean、
將任何 JDK class 顯示為 Web Bean、
為相同的實做 class 定義多重 Web Bean 以及不同的 scope 或初始化,或是
在 runtime 時改變一個 API 類型的實做。
特別是,producer method 讓我們可以同時使用 Web Bean 和執行期多型性(runtime polymorphism)。就如我們所見,建置類型(deployment type)對於建置期多型性(deployment-time polymorphism)的問題來說是個強大的解決方法。不過一旦系統被建置後,Web Bean 實做便會是故定的。Producer method 則沒有這方面的限制:
@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;
}
}
}
考慮這個注入點:
@Preferred PaymentStrategy paymentStrat;
這個注入點擁有和 producer method 相同的類型和綁定標記,因此它會使用一般的 Web Bean 注入規則來解析 producer method。Producer method 會被 Web Bean 管理員調用來取得一個 instance 來服務這個注入點。
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 物件不會被毀掉。