SeamFramework.orgCommunity Documentation

章 6. Producer method

6.1. producer method 的 scope
6.2. 注入 producer method
6.3. 使用 @New 和 producer method

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 能讓我們:

特別是,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;
    } 
}

接著有個新的 CreditCardPaymentStrategydependent instance 將會被建立、傳送至 producer method、被 producer method 回傳然後最後被綁定至 session context。在 Preferences 物件於 session 結束時被毀掉之前,dependent 物件不會被毀掉。