SeamFramework.orgCommunity Documentation

1장. Web Beans로 시작하기

1.1. 첫 번째 Web Bean
1.2. Web Bean은 무엇인가요?
1.2.1. API 유형, 바인딩 유형 및 의존성 삽입
1.2.2. 배치 유형
1.2.3. 범위
1.2.4. Web Bean 이름 및 Unified EL
1.2.5. 인터셉터 바인딩 유형
1.3. 어떤 종류의 객체가 Web Beans가 될 수 있을까요?
1.3.1. 심플 Web Beans
1.3.2. 엔터프라이즈 Web Beans
1.3.3. 생산자 방식
1.3.4. JMS 엔드포인트

첫 번째 Web Bean 작성을 시작해 보시겠습니까? 혹은 어떤 종류의 Web Beans 사양을 통해 도약하게 될 지를 의아해 하거나 의심하고 계실 수 도 있겠군요! 한 가지 좋은 정보는 여러분은 이미 수백개의 혹은 수천개의 Web Beans을 이미 작성 및 사용하고 계셨다는 것입니다. 아마 여러분은 처음으로 작성하신 Web Beans를 기억하지 못하고 계실 수 도 있습니다.

특정한 예외 상항에서 매개 변수가 없는 것을 허용하는 생성자와 함께하는 모든 Java 클래스는 Web Bean으로 이에는 모든 JavaBean이 포함됩니다. 이에 더하여 모든 EJB 3-스타일 세션 빈도 Web Bean입니다. 물론 매일 작성하셨던 JavaBeans 및 EJB는 Web Beans 사양에 의해 정의된 새로운 서비스의 장점을 취할 수 없지만, Web Beans으로서 기존 코드를 변경하지 않고 이 모든 것을 사용하실 수 있습니다 — 기타 다른 Web Beans로의 삽입, Web Beans XML 설정 기능을 통한 설정, 인터셉터 및 데코레이터 추가 등—

다양한 애플리케이션에서 오랫동안 사용해 온 두 개의 Java 클래스를 갖고 있다고 가정합시다. 첫 번째 클래스는 문자열을 문장 목록으로 구문 분석합니다:

public class SentenceParser {

    public List<String
> parse(String text) { ... }
}

두 번째 클래스는 하나의 언어에서 다른 언어로 문장을 번역할 수 있는 외부 시스템에 대한 무상태 세션 빈 프론트엔드입니다:

@Stateless

public class SentenceTranslator implements Translator {
    public String translate(String sentence) { ... }
}

여기서 Translator는 로컬 인터페이스입니다:

@Local

public interface Translator {
    public String translate(String sentence);
}

불행하게도 전체 텍스트 문서를 번역하는 기존 클래스가 없으므로, 이러한 작업을 실행하는 Web Bean을 작성합시다:

public class TextTranslator {

    
    private SentenceParser sentenceParser;
    private Translator sentenceTranslator;
    
    @Initializer
    TextTranslator(SentenceParser sentenceParser, Translator sentenceTranslator) {
        this.sentenceParser = sentenceParser;
        this.sentenceTranslator = sentenceTranslator;
    }
    
    public String translate(String text) {
        StringBuilder sb = new StringBuilder();
        for (String sentence: sentenceParser.parse(text)) {
            sb.append(sentenceTranslator.translate(sentence));
        }
        return sb.toString();
    }
    
}

인스턴스를 Web Bean, Servlet 또는 EJB로 삽입하여 TextTranslator 인스턴스를 얻을 수 있습니다:

@Initializer

public setTextTranslator(TextTranslator textTranslator) {
    this.textTranslator = textTranslator;
}

다른 방법으로 Web Beans 관리자 방식을 직접 호출하여 인스턴스를 획득할 수 있습니다:

TextTranslator tt = manager.getInstanceByType(TextTranslator.class);

하지만 잠시 기다려주십시오: TextTranslator는 매개 변수 없는 생성자를 갖지 않습니다! 그래도 이것이 Web Bean입니까? 클래스가 @Initializer라고 어노테이션된 생성자를 갖고 있을 경우 매개 변수 없이 생성자를 갖지 않는 클래스는 Web Bean이 될 수 있습니다.

예상하셨듯이, @Initializer 어노테이션은 의존성 삽입을 사용하여 실행하기 위한 무엇인가를 갖고 있습니다! @Initializer는 생성자 또는 Web Bean 방식에 적용될 수 있으며, Web Bean을 인스턴스화할 때 Web Bean 관리자에게 생성자 또는 Web Bean 방식을 호출할 것을 지시합니다. Web Bean 관리자는 기타 다른 Web Beans를 생성자 또는 방식의 매개 변수에 삽입하게 됩니다.

시스템 초기화시 Web Bean 관리자는 각각의 삽입 지점을 만족시키는 하나의 Web Bean이 존재하도록 이를 유효화해야 합니다. 예에서 사용 가능한 Translator 구현이 없을 경우 — SentenceTranslator EJB가 배치되지 않는 다면 — Web Bean 관리자는 UnsatisfiedDependencyException을 넘기게 됩니다. 하나 이상의 Translator를 구현할 수 있을 경우, Web Bean 관리자는 AmbiguousDependencyException을 넘기게 됩니다.

정확하게 Web Bean은 무엇인가요?

Web Bean은 비지니스 로직이 들어 있는 애플리케이션 클래스입니다. Web Bean은 Java 코드에서 직접 불러오거나 또는 Unified EL을 통해 불러올 수 있습니다. Web Bean은 트랜젝션 리소스에 액세스할 수 있습니다. Web Beans 사이에서의 종속성은 Web Bean 관리자에 의해 자동으로 관리됩니다. 대부분의 Web Beans은 상태 유지 (stateful)컨텍스트 기반입니다. Web Bean 수명 주기는 항상 Web Bean 관리자에 의해 관리됩니다.

두 번째 내용으로 돌아가 봅시다. "컨텍스트화"된다는 의미는 정확하게 무엇입니까? Web Beans가 상태 유지 세션 빈이면, 보유하고 있는것이 어떤 빈 인스턴스인가가 문제입니다. 무상태 구성 요소 모델 이나 (예: 무상태 세션 빈) 또는 singleton 구성요소 모델 (예: ervlets 또는 singleton 빈)과는 다르게 Web Bean의 다른 클라이언트는 다른 상태에 있는 Web Bean을 보게 됩니다. 클라이언트-가시성 상태는 클라이언트가 참조하고 있는 Web Bean의 인스턴스가 무엇인가에 의존합니다.

하지만, 무상태 또는 싱글턴 모델과 같으나 상태 유지 세션 빈 과는 다르게, 클라이언트는 인스턴스의 수명 주기를 명시적으로 생성 또는 파괴하여 제어하지 못합니다. 대신, Web Bean의 범위는 다음 사항을 결정합니다:

Web Beans 애플리케이션에 있는 주어진 스레드의 경우, Web Bean 범위와 관련된 활성 컨텍스트가 있을 수 있습니다. 이러한 컨텍스트는 스레드에서 유일하거나 (예: 요청 범위일 경우) 또는 특정한 다른 스레드와 공유될 수 있거나 (예: Web Bean이 세션 범위일 경우) 또는 기타 다른 모든 스레드일 수 있습니다 (Web Bean이 애플리케이션 범위일 경우).

동일한 컨텍스트에서 실행되는 클라이언트 (예: 기타 다른 Web Bean)는 동일한 Web Bean의 인스턴스를 보게 됩니다. 하지만 다른 컨텍스트에서 실행되는 클라이언트는 다른 인스턴스를 보게 됩니다.

컨텍스트 모델의 큰 장점 중 하나는 상태 유지 Web Beans가 서비스처럼 다루어지게 하는 것입니다! 클라이언트는 사용되는 Web Bean의 수명 주기 관리 자체를 고려하거나, 또는 수명 주기가 무엇인지를 알 필요가 없습니다. Web Beans는 메세지를 전달하여 상호작용하고 Web Bean 구현은 자신의 상태에 따른 수명 주기를 정의합니다. Web Beans는 다음과 같은 이유로 느슨하게 연결되어 있습니다:

하나의 Web Bean을 동일한 API를 구현하고 다른 Web Bean 구현에 영향을 미치지 않고 다른 수명 주기 (다른 범위)를 갖는 다른 Web Bean으로 대체할 수 있습니다. 사실, 4.2절. “배치 유형 ”에서 살펴보실 수 있듯이 Web Beans는 배치시 Web Bean 구현을 덮어쓰기하기 위한 정교한 기능을 정의합니다.

모든 Web Bean 클라이언트가 Web Beans가 될 수 없음에 유의합니다. Servlets 또는 Message-Driven Beans와 같은 — 본래 삽입 가능한 컨텍스트 객체가 아닌 — 객체는 삽입에 의해 Web Beans에 참조를 획득할 수 있습니다.

보다 공식적으로 spec에 따르면:

Web Bean은 다음과 같은 것으로 구성되어 있습니다:

  • (비어 있지 않은) API 유형 모음

  • (비어 있지 않은) 바인딩 어노테이션 유형 모음

  • 범위

  • 배치 유형

  • Web Bean 이름 (옵션 사항)

  • 인터셉터 바인딩 유형 모음

  • Web Bean 구현

이러한 용어가 Web Bean 개발자에게 무엇을 의미하는 살펴봅시다.

주로 Web Beans는 의존성 삽입을 통해 다른 Web Beans에 참조를 획득합니다. 삽입된 속성은 삽입될 Web Bean을 만족시키는 "계약"을 지정합니다. 계약에는 다음과 같은 내용을 지정합니다:

API는 사용자 정의 클래스 또는 인터페이스입니다. (Web Bean이 EJB 세션 빈일 경우, API 유형은 @Local 인터페이스이거나 또는 bean-class 로컬 보기입니다.) 바인딩 유형은 클라이언트-가시성 시멘틱을 나타내는 것으로 API의 일부 구현에 의해 만족됩니다.

바인딩 유형은 @BindingType으로 어노테이션된 사용자 정의 어노테이션에 의해 나타납니다. 예를 들어, 다음과 같은 삽입 지점은 PaymentProcessor API 유형 및 @CreditCard 바인딩 유형을 갖습니다:

@CreditCard PaymentProcessor paymentProcessor

바인딩 유형이 삽입 지점에 명확하게 지정되어 있지 않을 경우, 기본값 바인딩 유형@Current가 사용됩니다.

각각의 삽입 지점의 경우, Web Bean 관리자는 계약을 만족시키는 (API를 구현하고 모든 바인딩 유형을 갖는) Web Bean을 검색하여 이를 삽입합니다.

다음과 같은 Web Bean은 @CreditCard 바인딩 유형을 갖고 PaymentProcessor API 유형을 구현합니다. 따라서 이는 예시 삽입 지점에 삽입될 수 있습니다:

@CreditCard

public class CreditCardPaymentProcessor 
    implements PaymentProcessor { ... }

Web Bean이 바인딩 유형 모음을 명확히 지정하지 않을 경우, 이는 기본값 바인딩 유형인 @Current라는 하나의 바인딩 유형을 갖습니다.

Web Beans는 정교하지만 직관적인 해상도 알고리즘을 정의하여 특정 계약을 만족시키는 하나 이상의 Web Bean이 있을 경우 컨테이너가 무엇을 해야할 지를 결정하는 것을 돕습니다. 보다 자세한 내용은 4장. 의존성 삽입 에서 다루겠습니다.

JavaBeans, EJB 및 기타 다른 Java 클래스가 Web Beans이 될 수 있음을 살펴보았습니다. 하지만, 정확히 어떤 종류의 객체가 Web Beans인가요?

사양에서는 모든 EJB 3 스타일 세션 및 singleton beans가 EJB 3 엔터프라이즈 Web Beans라고 말합니다. Message driven beans는 Web Beans이 아닙니다 — 다른 객체에 삽입되도록 되어있지 않기 때문 — 하지만 이는 의존성 삽입 및 인터셉터를 포함한 대부분의 Web Beans 기능의 장점을 취할 수 있습니다.

와일드카드 유형 매개 변수 또는 유형 변수를 갖지 않는 엔터프라이즈 Web Bean의 모든 로컬 인터페이스 및 이의 상위인터페이스는 엔터프라이즈 Web Bean의 API 유형입니다. EJB bean이 bean 클래스 로컬 보기를 갖고 있을 경우 bean 클래스 및 이의 모든 상위 클래스도 API 유형입니다.

상태유지 세션 빈은 매개 변수가 없는 제거 방식이나 @Destructor로 어노테이션된 삭제 방식을 명시해야 합니다. Web Bean 관리자는 수명 주기의 마지막에 상태 유지 세션 빈 인스턴스를 파괴하기 위해 이러한 방식을 호출합니다. 이러한 방식은 엔터프라이즈 Web Bean의 파괴자 방식이라고 합니다.

@Stateful @SessionScoped

public class ShoppingCart {
    ...
    
    @Remove
    public void destroy() {}
}

그러면 언제 심플 Web Bean 대신 엔터프라이즈 Web Bean을 사용해야 할까요? 다음과 같이 EJB에 의해 제공되는 고급 엔터프라이즈 서비스가 필요할 때 마다 사용하면 됩니다:

엔터프라이즈 Web Bean을 사용해야 합니다. 이러한 것이 전혀 필요하지 않을 경우, 심플 Web Bean도 잘 실행될 것입니다.

다수의 Web Beans (세션 또는 애플리케이션 범위 Web Bean 포함)은 동시 액세스에서 사용 가능합니다. 따라서, EJB 3.1에 의해 제공되는 동시성 관리는 아주 유용합니다. 대부분의 세션 및 애플리케이션 범위 Web Beans는 EJB이어야 합니다.

Web Beans는 중량의 리소스에 참조를 보유하고 있거나 EJB @Stateless/@Stateful/@Singleton 모델에 의해 정의된 고급 컨테이너 관리 수명주기로 부터의 내부적 상태 장점을 수동화 및 인스턴스 풀링 지원과 함께 보유하고 있어야 합니다.

마지막으로, 메서드-수준 트랜젝션 관리, 메서드-수준 보안, 타이머, 원격 방식 또는 비동기 방식은 명백하게 필요합니다.

심플 Web Bean으로 시작하고, @Stateless, @Stateful, @Singleton 어노테이션을 추가하여 EJB로 변환합니다.

생산자 방식은 현재 컨텍스트에 인스턴스가 존재하지 않을 때 Web Bean의 인스턴스를 획득하기 위해 Web Bean 관리자에 의해 호출되는 방식입니다. 생산자 방식은 인스턴스를 Web Bean 관리자가 관리하게 두지 않고 애플리케이션이 인스턴스 절차를 완전히 제어하게 합니다. 예:

@ApplicationScoped

public class Generator {
    private Random random = new Random( System.currentTimeMillis() );
    
    @Produces @Random int next() {
        return random.nextInt(100);
    }
}

생산자 방식 결과는 기타 다른 Web Bean과 같이 삽입됩니다.

@Random int randomNumber

방식 반환 유형 및 직접적이나 간접적으로 확장/구현하는 모든 인터페이스 API 유형의 생산자 방식입니다. 반환 유형이 클래스일 경우 모든 상위클래스도 API 유형이 됩니다.

일부 생산자 방식은 명시적 파괴를 요청하는 객체를 반환합니다:

@Produces @RequestScoped Connection connect(User user) {

    return createConnection( user.getId(), user.getPassword() );
}

이러한 생산자 방식은 폐지 방식과 일치하게 정의될 수 있습니다:

void close(@Disposes Connection connection) {

    connection.close();
}

이러한 폐지 방식은 요청 마지막에 Web Bean 관리자에 의해 자동으로 호출됩니다.

6장. 생산자 방식 에서 생산자 방식에 관해 보다 자세하게 다룹니다.