SeamFramework.orgCommunity Documentation

6장. 생산자 방식

6.1. 생산자 방식의 범위
6.2. 생산자 방식으로 삽입
6.3. 생산자 방식과 함께 @New 사용

생산자 방식은 Web Bean 관리자가 애플리케이션 대신 인스턴스 객체에 응답할 때 발생할 수 있는 특정한 한계를 극복할 수 있게 합니다. Web Beans이 아닌 객체를 Web Beans 환경으로 통합하기 위한 가장 쉬운 방법이 있습니다. (12장. XML을 사용하여 Web Beans 정의 에서 두번째 방법에 대해 살펴보겠습니다.)

spec에 의하면:

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:

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.

.

생산자 방식의 기본값 범위는 @Dependent로 되어 있으므로, 매 번 Web Bean 관리자가 이러한 영역 또는 동일한 생산자 방식으로 해결되는 다른 영역을 삽입할 때 마다 호출됩니다. 따라서, 각각의 사용자 세션에 대해 PaymentStrategy 객체의 인스턴스가 여러개 있을 수 있습니다.

이러한 기능을 변경하려면, @SessionScoped 어노테이션 방식을 추가할 수 있습니다.

@Produces @Preferred @SessionScoped

public PaymentStrategy getPaymentStrategy() {
    ...
}

이제 생산자 방식이 호출되면, 반환된 PaymentStrategy는 세션 컨텍스트로 바운드됩니다. 동일한 세션에서 생산자 방식은 다시 호출되지 않습니다.

위의 코드에 한 가지 문제가 있을 수 있습니다. CreditCardPaymentStrategy 구현은 Java new 연산자를 사용하여 인스턴스화됩니다. 애플리케이션에 의해 직접 인스턴스화된 객체는 의존성 삽입의 장점을 취할 수 없으며 인터셉터를 갖지 못합니다.

이러한 방식을 원하지 않을 경우, 의존성 삽입을 생산자 방식으로 사용하여 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;
    } 
}

CreditCardPaymentStrategy가 요청 범위 Web Bean이면 어떻게 합니까? 이럴 경우 생산자 방식은 현재 요청 범위 임스턴스를 세선 범위로 "활성화되게" 됩니다. 이는 버그입니다! 요청 범위 객체는 세션이 종료되기 전 Web Bean 관리자에 의해 삭제되지만, 객체에 대한 참조는 세션 범위에 "남아 있게" 됩니다. 이러한 오류는 Web Bean 관리자에 의해 검출되지 않으므로 생산자 방식에 Web Bean 인스턴스를 반환할 경우 각별히 주의하시기 바랍니다!

이러한 버그를 해결할 수 있는 세 가지 방법이 있습니다. CreditCardPaymentStrategy 구현 범위를 변경할 수 있지만, Web Bean의 클라이언트에 영향을 미칠 수 있습니다. 보다 나은 방법으로 생산자 방식의 범위를 @Dependent 또는 @RequestScoped로 변경하는 것입니다.

하지만 보다 일반적인 방법은 특별한 @New 바인딩 어노테이션을 사용하는 것입니다.

다음과 같은 생산자 방식을 고려합니다:

@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의 새로운 의존성 인스턴스가 생성되면, 생산자 방식으로 전달하고, 생산자 방식에 의해 반환된 후, 마지막으로 세션 컨텍스트로 바운딩됩니다. 의존성 객체는 세션 마지막에 Preferences 객체가 삭제될 때 까지 삭제되지 않습니다.