SeamFramework.orgCommunity Documentation
Web Beans는 의존성 삽입에 대한 세 가지 주요 메카니즘을 지원합니다:
생성자 매개 변수 삽입:
public class Checkout {
private final ShoppingCart cart;
@Initializer
public Checkout(ShoppingCart cart) {
this.cart = cart;
}
}
초기화 방식 매개 변수 삽입:
public class Checkout {
private ShoppingCart cart;
@Initializer
void setShoppingCart(ShoppingCart cart) {
this.cart = cart;
}
}
직접적 영역 삽입:
public class Checkout {
private @Current ShoppingCart cart;
}
Web Bean 인스턴스가 먼저 인스턴스화되었을 경우 의존성 삽입이 발생합니다.
먼저, Web Bean 관리자는 Web Bean 생성자를 호출하여 Web Bean 인스턴스를 획득합니다.
다음으로, Web Bean 관리자는 Web Bean의 삽입된 모든 영역의 값을 초기화합니다.
그 후, Web Bean 관리자는 Web Bean의 모든 초기화 방식을 호출합니다.
마지막으로 Web Bean의 @PostConstruct
방식이 호출됩니다.
EJB가 Web Bean 관리자가 아닌 EJB 컨테이너에 의해 인스턴스화된 이래 생성자 매개변수 삽입은 EJB beans을 지원하지 않습니다.
기본값 바인딩 유형 @Current
가 적용되었을 경우 생성자의 매개 변수 및 초기화 방식에서는 명시적으로 어노테이션할 필요가 없습니다. 하지만 삽입된 영역에는 반드시 바인딩 유형을 지정해야 하며, 언제 기본값 바인딩 유형을 적용할 지도 지정해야 합니다. 영역에 바인딩 유형이 지정되어 있지 않을 경우, 이는 삽입되지 않게 됩니다.
생산자 방식은 매개 변수 삽입을 지원합니다:
@Produces Checkout createCheckout(ShoppingCart cart) {
return new Checkout(cart);
}
마지막으로 옵저버 방식 (9장. 이벤트 에서 살펴보게됨), 폐지 방식 및 소멸자 방식 모두는 매개 변수 삽입을 지원합니다.
Web Beans 사양은 타입 안정적 해상도 알고리즘이라는 절차를 정의하여, Web Bean 관리자가 삽입 지점에 삽입하기 위해 Web Bean을 인식할 때 이를 따르게 됩니다. 이러한 알고르짐은 처음에는 복잡하게 보이지만, 일단 이를 이해하면, 이는 다소 직관적입니다. 타입 안정적 해상도는 시스템 초기화시에 실행되어, 관리자는 사용자에 과한 정보를 즉각적으로 알게되며 Web Bean의 의존성에 만족하지 않을 경우, UnsatisfiedDependencyException
또는 AmbiguousDependencyException
을 넘기게 됩니다.
이러한 알고리즘은 여러 Web Beans를 허용하여 동일한 API 유형을 구현하기 위한 것입니다:
또는 클라이언트가 바인딩 어노테이션을 사용하여 필요한 구현 사항을 선택하게 합니다,
애플리케이션 개발자가 클라이언트로 변경하지 않고 배치 유형을 활성화 또는 비활성화하여 특정 시스템에 어떤 구헌이 적합할 지를 선택하게 합니다.
하나의 API 구현이 클라이언트에 변경하지 않고 배치 유형 우선 순위를 사용하여 배치시 다른 동일한 API 구현을 덮어쓰기하게 합니다.
Web Beans 관리자가 어떻게 Web Bean을 삽입할 지를 결정하는 방법에 대해 살펴봅시다.
특정 API 유형을 구현하는 하나 이상의 Web Bean이 있을 경우, 삽입 지점은 바인딩 어노테이션을 사용하여 어떤 Web Bean이 삽입되어야 할 지를 지정할 수 있습니다. 예를 들어, PaymentProcessor
의 두 가지 구현이 있을 수 있습니다:
@PayByCheque
public class ChequePaymentProcessor implements PaymentProcessor {
public void process(Payment payment) { ... }
}
@PayByCreditCard
public class CreditCardPaymentProcessor implements PaymentProcessor {
public void process(Payment payment) { ... }
}
여기서 @PayByCheque
및 @PayByCreditCard
는 바인딩 어노테이션입니다:
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
@BindingType
public @interface PayByCheque {}
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
@BindingType
public @interface PayByCreditCard {}
클라이언트 Web Bean 개발자는 바인딩 어노테이션을 사용하여 정확히 어떤 Web Bean이 삽입되어야 하는 지를 지정합니다.
영역 삽입 사용:
@PayByCheque PaymentProcessor chequePaymentProcessor;
@PayByCreditCard PaymentProcessor creditCardPaymentProcessor;
초기화 방식 삽입 사용:
@Initializer
public void setPaymentProcessors(@PayByCheque PaymentProcessor chequePaymentProcessor,
@PayByCreditCard PaymentProcessor creditCardPaymentProcessor) {
this.chequePaymentProcessor = chequePaymentProcessor;
this.creditCardPaymentProcessor = creditCardPaymentProcessor;
}
생성자 삽입 사용:
@Initializer
public Checkout(@PayByCheque PaymentProcessor chequePaymentProcessor,
@PayByCreditCard PaymentProcessor creditCardPaymentProcessor) {
this.chequePaymentProcessor = chequePaymentProcessor;
this.creditCardPaymentProcessor = creditCardPaymentProcessor;
}
바인딩 어노테이션은 멤버를 갖을 수 있습니다:
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
@BindingType
public @interface PayBy {
PaymentType value();
}
이러한 경우, 멤버 값이 중요합니다:
@PayBy(CHEQUE) PaymentProcessor chequePaymentProcessor;
@PayBy(CREDIT_CARD) PaymentProcessor creditCardPaymentProcessor;
Web Bean 관리자에게 @NonBinding
멤버를 어노테이션하여 바인딩 어노테이션 유형 멤버를 무시하라고 말할 수 있습니다.
삽입 지점은 여러 바인딩 어노테이션을 지정할 수 도 있습니다:
@Asynchronous @PayByCheque PaymentProcessor paymentProcessor
이러한 경우, 양쪽 바인딩 어노테이션을 갖는 Web Bean만 삽입될 수 있습니다.
생산자 방식은 바인딩 어노테이션을 지정할 수 도 있습니다:
@Produces
@Asynchronous @PayByCheque
PaymentProcessor createAsyncPaymentProcessor(@PayByCheque PaymentProcessor processor) {
return new AsynchronousPaymentProcessor(processor);
}
모든 Web Beans는 배치 유형을 갖습니다. 각각의 배치 유형은 시스템의 일부 배치에 조건부로 설치되어야 하는 Web Beans 모음을 인식합니다.
예를 들어, @Mock
라는 배치 유형을 정의할 수 있으며, 이는 시스템이 통합적인 테스트 환경 내에서 실행될 때 설치되어야 하는 Web Beans를 인식합니다:
@Retention(RUNTIME)
@Target({TYPE, METHOD})
@DeploymentType
public @interface Mock {}
지불 절차를 처리하기 위한 외부적 시스템과 상호 작용하는 일부 Web Bean이 있다고 가정합시다:
public class ExternalPaymentProcessor {
public void process(Payment p) {
...
}
}
이러한 Web Bean이 배치 유형을 명시적을 지정하지 않고 있으므로, 이는 기본값 배치 유형 @Production
을 갖게 됩니다.
통합 또는 단위 테스트를 위해 외부적 시스템은 속도가 느리거나 또는 사용 불가능합니다. 따라서 모의 객체를 생성할 수 있습니다:
@Mock
public class MockPaymentProcessor implements PaymentProcessor {
@Override
public void process(Payment p) {
p.setSuccessful(true);
}
}
하지만 Web Bean 관리자가 특정 배치에 어떤 구현을 사용할 지를 어떻게 결정할까요?
Web Beans는 두 개의 내장된 배치 유형을 정의합니다: @Production
및 @Standard
. 시스템이 배치되면 기본값으로 내장된 배치 유형과 함께 Web Beans 만이 활성화됩니다. web-beans.xml
에서 목록을 나열하여 특정 배치에서 활성화될 추가 배치 유형을 인식할 수 있습니다.
예제로 다시 돌아가서, 통합적 테스트를 배치할 때, 모든 @Mock
객체가 설치되기를 원합니다:
<WebBeans>
<Deploy>
<Standard/>
<Production/>
<test:Mock/>
</Deploy>
</WebBeans
>
이제 Web Bean 관리자는 배치시 @Production
, @Standard
, @Mock
어노테이션된 모든 Web Beans를 인식 및 설치하게 됩니다.
@Standard
배치 유형은 Web Beans 사양에 의해 정의된 특정 Web Beans에서만 사용됩니다. 자체적 Web Beans에는 이를 사용할 수 없으며 이를 비활성화할 수 없습니다.
@Production
배치 유형은 배치 유형을 명시하지 않은 Web Beans에 대한 기본값 배치 유형으로 비활성화될 수 있습니다.
관심을 기울이셨다면, Web Bean 관리자가 어떤 구현 ExternalPaymentProcessor
또는 MockPaymentProcessor
을 선택할 지를 어떻게 결정하는지에 대해 궁금해 하셨을 것입니다. 관리자기 이러한 삽입 지점에 이르렀을 때 무엇이 발생할 지를 고려합니다:
@Current PaymentProcessor paymentProcessor
PaymentProcessor
계약을 만족하는 두 개의 Web Beans가 있습니다. 삽입 지점에서 바인딩 어노테이션은 소스로 하드-코드화되어 있으므로, 명확하게 하기 위해 바인딩 어노테이션을 사용할 수 없으며, 관리자가 배치시 결정할 수 있게 하기를 원합니다!
이러한 문제의 해결책은 각각의 배치 유형이 다른 우선 순위를 갖게 하는 것입니다. 배치 유형의 우선 순위는 web-beans.xml
에 나타나는 순서에 의해 결정됩니다. 예에서 @Mock
는 @Production
보다 나중에 나타나므로 보다 높은 우선 순위를 갖습니다.
하나 이상의 Web Bean이 삽입 지점에 의해 지정된 계약 (API 유형 및 바인딩 어노테이션)에 만족할 수 있음을 관리자가 발견할 때 마다, 이는 Web Beans의 관련된 우선 순위를 고려합니다. 하나의 Web Bean이 다른 것 보다 높은 우선 순위를 갖을 경우, 삽입될 보다 높은 우선 순위의 Web Bean을 선택합니다. 예에서, 통합 테스트 환경 (실제적으로 원하는 환경)에서 실행할 때 Web Bean 관리자는 MockPaymentProcessor
를 삽입하게 됩니다.
이러한 기능을 오늘날의 관리자 구조와 비교하는 것은 흥미로운 일입니다. 다양한 "경량"의 컨테이너는 classpath에 있는 클래스의 조건부 배치를 허용하지만, 배치될 클래스는 명시적, 개별적이어하며, 설정 코드나 일부 XML 설정 파일에 나열되어 있어야 합니다. Web Beans는 XML을 통한 Web Bean 정의 및 설정을 지원하지만 복잡한 설정을 필요로 하지 않는 대부분의 경우, 배치 유형은 전체 Web Beans 설정이 단일 XML 행으로 활성화되게 합니다. 동시에, 코드를 검색하는 개발자는 어떤 배치 시나리오를 Web Bean이 사용할 지를 쉽게 인식할 수 있습니다.
바인딩 어노테이션 및 삽입 지점의 API 유형을 구현하는 모든 Web Beans의 배치 유형을 고려한 후, Web Bean 관리자는 삽입할 하나의 Web Bean을 인식하지 못할 경우 타입 안정적 해상도 알고리즘은 실패하게 됩니다.
UnsatisfiedDependencyException
또는 AmbiguousDependencyException
을 수정하기가 쉽습니다.
UnsatisfiedDependencyException
을 수정하려면, API 유형을 구현하는 Web Bean을 제공하고 삽입 지점의 바인딩 유형을 갖거나 또는 API 유형을 이미 구현하고 있는 Web Bean의 배치 유형을 활성화하고 바인딩 유형을 갖습니다.
AmbiguousDependencyException
을 수정하려면, 두 개의 API 유형 구현 사이에서 구별하기 위해 바인딩 유형을 소개하거나 구현 중 하나의 배치 유형을 변경하여 Web Bean 관리자가 이들 사이에서 선택하기 위해 배치 유형 우선 순위를 사용할 수 있습니다. AmbiguousDependencyException
은 두 개의 Web Beans가 바인딩 유형을 공유하여 동일한 배치 유형을 갖고 있을 때에만 발생할 수 있습니다.
Web Beans에서 의존성 삽입을 사용할 때 유의하셔야 할 사항이 한 가지 더 있습니다.
삽입된 Web Bean 클라이언트는 Web Bean 인스턴스에 직접적인 참조를 유지하지 않습니다.
요청 범위로 바운딩된 Web Bean으로의 직접 참조를 보유하고 있는 애플리케이션 범위에 바운딩된 Web Bean을 가정해 봅시다. 애플리케이션 범위에 바운딩된 Web Bean은 다른 많은 요청 사이에서 공유됩니다. 하지만, 각각의 요청은 요청 범위로 바운딩된 Web Bean의 다른 인스턴스를 확인해야 합니다!
이제 애플리케이션 범위로 바운딩된 Web Bean으로의 직접 참조를 보유하고 있는 세션 범위에 바운딩된 Web Bean을 가정해 봅시다. 때때로 세션 컨텍스트는 메모리를 보다 효과적으로 사용하기 위해 디스크 순서대로 나열됩니다. 하지만, 애플리케이션 범위로 바운딩된 Web Bean 인스턴스는 세션 범위로 바운딩된 Web Bean과 함께 나열되어서는 안됩니다!
따라서, Web Bean은 기본값 범위 @Dependent
를 갖지 않는 한, Web Bean 관리자는 프록시 객체를 통해 삽입된 모든 참조를 Web Bean으로 돌려 놓아야 합니다. 이러한 클라이언트 프록시는 방식 호출을 받는 Web Bean 인스턴스가 현재 컨텍스트와 관련된 인스턴스임을 확인해야 할 책임이 있습니다. 또한 클라이언트 프록시는 Web Beans가 삽입된 다른 Web Beans를 귀납적으로 나열하지 않고 디스크 순서대로 나열한 세션 컨텍스트와 같은 컨텍스트로 바운딩되게 합니다.
Java 언어의 한계로 인해, 일부 Java 유형은 Web Bean 관리자에 의해 프록시될 수 없습니다. 따라서, 삽입 지점 유형이 프록시될 수 없을 경우 Web Bean 관리자는 UnproxyableDependencyException
을 던지게 됩니다.
다음과 같은 Java 유형은 Web Bean 관리자에 의해 프록시될 수 없습니다:
final
이라고 명시되어 있거나 또는 final
방식을 갖는 클래스,
매개 변수 없는 비전용 생성자를 갖지 않는 클래스,
배열 및 기본 유형
UnproxyableDependencyException
을 수정하기 쉽습니다. 간단하게 삽입된 클래스에 매개 변수가 없는 생성자를 추가하고, 인터페이스를 소개하거나, 삽입된 Web Bean의 범위를 @Dependent
로 변경합니다.
애플리케이션은 삽입에 의해 Manager
인터페이스의 인스턴스를 획득할 수 있습니다:
@Current Manager manager;
Manager
객체는 프로그램적으로 Web Bean 인스턴스의 획득을 위한 방식 모음을 제공합니다.
PaymentProcessor p = manager.getInstanceByType(PaymentProcessor.class);
바인딩 어노테이션은 AnnotationLiteral
도우미 클래스를 하부클래스화하여 지정될 수 있으며, 그렇지 않으면 Java에서 어노테이션 유형을 인스턴스화하기 어렵습니다.
PaymentProcessor p = manager.getInstanceByType(PaymentProcessor.class,
new AnnotationLiteral<CreditCard
>(){});
바인딩 유형이 어노테이션 멤버를 갖을 경우, AnnotationLiteral
의 임의의 하부클래스를 사용할 수 없습니다 대신 named 하부 클래스를 생성해야 합니다:
abstract class CreditCardBinding
extends AnnotationLiteral<CreditCard
>
implements CreditCard {}
PaymentProcessor p = manager.getInstanceByType(PaymentProcessor.class,
new CreditCardBinding() {
public void value() { return paymentType; }
} );
Enterprise Web Beans는 EJB 사양에 의해 정의된 모든 수명 주기 콜백을 지원합니다: @PostConstruct
, @PreDestroy
, @PrePassivate
및 @PostActivate
.
심플 Web Beans는 @PostConstruct
및 @PreDestroy
콜백 기능만을 지원합니다.
엔터프라이즈 및 심플 Web Beans는 Java EE 리소스 삽입, EJB 및 JPA 영구적 컨텍스에 대해 @Resource
, @EJB
및 @PersistenceContext
의 사용을 각각 지원합니다. 심플 Web Beans는 @PersistenceContext(type=EXTENDED)
의 사용을 지원하지 않습니다.
@PostConstruct
콜백은 모든 의존성이 삽입된 후에 나타납니다.
특정 종류의 의존성 객체가 있습니다 @Dependent
범위와 함께 Web Beans 객체 또는 이는 실행 가능하게 되기 위해 삽입되는 삽입 지점에 관해 알아야 합니다. 예:
Logger
용 로그 카테고리는 소유하고 있는 객체 클래스에 의존합니다.
헤더 값이나 HTTP 매개 변수 삽입은 삽입 지점에서 어떤 매개 변수 또는 헤더 이름을 지정하였는가에 의존합니다.
EL 표현식 평가 (expression evaluation) 평가의 삽입은 삽입 지점에서 지정된 표현식에 의존합니다.
@Dependent
범위와 함께 Web Bean은 InjectionPoint
인스턴스를 삽입하고 해당 삽입 지점에 관련된 메타데이트를 액세스합니다.
다음 예를 살펴봅시다. 다음과 같은 코드는 프로그램을 리팩토링하기에 취약하고 장황합니다:
Logger log = Logger.getLogger(MyClass.class.getName());
이와 같이 현명한 생산자 방식은 로그 범주를 명시적으로 지정하지 않고 JDK Logger
를 삽입하게 합니다:
class LogFactory {
@Produces Logger createLogger(InjectionPoint injectionPoint) {
return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
}
}
이제 다음을 작성할 수 있습니다:
@Current Logger log;
확신하시지 않습니까? 그러시다면 여기에 두 번째 예제가 있습니다. HTTP 매개 변수를 삽입하려면, 바인딩 유형을 정의해야 합니다:
@BindingType
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface HttpParam {
@NonBinding public String value();
}
다음과 같이 삽입 지점에서 이러한 바인딩 유형을 사용할 수 있습니다:
@HttpParam("username") String username;
@HttpParam("password") String password;
다음의 생산자 방식은 다음과 같이 작업합니다:
class HttpParams
@Produces @HttpParam("")
String getParamValue(ServletRequest request, InjectionPoint ip) {
return request.getParameter(ip.getAnnotation(HttpParam.class).value());
}
}
HttpParam
어노테이션의 value()
멤버는@NonBinding.
을 어노테이션하므로 Web Bean 관리자에 의해 무시됨에 유의하시기 바랍니다.
Web Bean 관리자는 InjectionPoint
인터페이스를 구현하는 내장된 Web Bean을 제공합니다:
public interface InjectionPoint {
public Object getInstance();
public Bean<?> getBean();
public Member getMember():
public <T extends Annotation
> T getAnnotation(Class<T
> annotation);
public Set<T extends Annotation
> getAnnotations();
}