SeamFramework.orgCommunity Documentation
Web Beans 이벤트 통지 기능은 Web Beans가 완전 연결 해제 방식으로 상호 작용하게 합니다. 이벤트 생산자는 이벤트를 제기한 후 Web Bean 관리자에 의해 이벤트 옵저버로 전달합니다. 이러한 기본적인 스키마는 옵저버/옵저버 패턴과 유사하게 들이지만, 몇 가지 다른 부분이 있습니다:
이벤트 생산자만 옵저버에서 연결 해제되는 것이 아니라; 옵저버도 생산자에서 완전하게 연결 해제됩니다.
옵저버는 "선택자" 조합을 지정하여 이벤트 통지 모음의 범위를 좁힙니다.
옵저버는 바로 통지하거나 또는 현재 트랜젝션이 끝날때 까지 이벤트 전송이 지연되도록 지정할 수 있습니다.
옵저버 방식은 매개변수 어노테이션 @Observes
를 사용하는 Web Bean 방식입니다.
public void onAnyDocumentEvent(@Observes Document document) { ... }
어노테이션된 매개 변수는 이벤트 매개 변수라고 부릅니다. 이벤트 매개 변수의 유형은 옵저버된 이벤트 유형입니다. 옵저버 방식은 Web Beans 바인딩 유형의 인스터스인 "선택자"를 지정할 수 있습니다. 바인딩 유형이 이벤트 선택자로 사용될 경우, 이를 이벤트 바인딩 유형이라고 부릅니다.
@BindingType
@Target({PARAMETER, FIELD})
@Retention(RUNTIME)
public @interface Updated { ... }
이벤트 매개 변수를 어노테이션하여 옵저버 방식의 이벤트 바인딩을 지정합니다:
public void afterDocumentUpdate(@Observes @Updated Document document) { ... }
옵저버 방식은 어떤 이벤트 바인딩도 지정할 필요가 없습니다 이러한 경우 특정 유형의 모든 이벤트에 관심을 갖게 됩니다. 이벤트 바인딩을 지정하지 않을 경우, 이러한 이벤트 바인딩이 있는 이벤트에만 관심을 갖게 됩니다.
옵저버 방식은 추가 매개 변수를 갖을 수 있으며, 이는 일반적인 Web Beans 방식 매개 변수 삽입 시멘틱에 따라 삽입됩니다:
public void afterDocumentUpdate(@Observes @Updated Document document, User user) { ... }
이벤트 생산자는 삽입을 통해 이벤트 통지 객체를 갖을 수 있습니다:
@Observable Event<Document
> documentEvent
@Observable
어노테이션은 @Dependent
범위 및 @Standard
배치 유형, Web Bean 관리자에 의해 제공되는 구현으로 Web Bean을 정의합니다.
생산자는 Event
인터페이스의 fire()
방식을 호출하여 이벤트를 제기하고, 이벤트 객체를 전달합니다:
documentEvent.fire(document);
이벤트 객체는 와일드카드 유형 매개변수나 또는 유형 변수가 없는 Java 클래스의 인스턴스가 될 수 도 있습니다. 이벤트는 다음과 같은 사항을 갖는 모든 옵저버 방식에 전달됩니다:
이벤트 객체를 할당할 수 있는 이벤트 매개 변수를 갖습니다,
이벤트 바인딩 없음을 지정합니다.
Web Bean 관리자는 모든 옵저버 방식을 호출하여, 이벤트 매개 변수 값으로 이벤트 객체를 전달합니다. 옵저버 방식이 예외 처리를 넘기게 될 경우, Web Bean 관리자는 옵저버 방식 호출을 중단하고 예외 처리는 fire()
방식에 의해 다시 넘기게 됩니다.
"선택자"를 지정하려면, 이벤트 생산자가 이벤트 바인딩 유형을 fire()
방식에 전달해야 합니다:
documentEvent.fire( document, new AnnotationLiteral<Updated
>(){} );
Java에서 실행하기 어려웠던 도우미 클래스 AnnotationLiteral
은 바인딩 유형 인라인을 인스턴스화할 수 있게 합니다.
이벤트는 모든 옵저버 방식으로 전달됩니다:
이벤트 객체를 할당할 수 있는 이벤트 매개 변수를 갖습니다,
이는 fire()
에 전달된 이벤트 바인딩에 대한 모든 이벤트 바인딩 예외 사항을 지정하지 않습니다.
다른 방법으로 이벤트 바인딩은 이벤트 통지 삽입 지점을 어노테이션하여 지정될 수 도 있습니다:
@Observable @Updated Event<Document
> documentUpdatedEvent
이러한 Event
인스턴스를 통해 해제된 모든 이벤트는 어노테이션된 이벤트 바인딩을 갖습니다. 이벤트는 모든 옵저버 방식에 전달됩니다:
이벤트 객체를 할당할 수 있는 이벤트 매개 변수를 갖습니다,
이는 이벤트 통지 삽입 지점의 어노트에션된 이벤트 바인딩이나 또는 fire()
에 전달된 이벤트 바인딩에 대한 모든 이벤트 바인딩 예외 사항을 지정하지 않습니다.
종종 이벤트 옵저버를 동적으로 등록하는 것이 유용합니다. 애플리케이션은 Observer
인터페이스를 구현하고 observe()
방식을 호출하여 이벤트 통지와 함께 인스턴스를 등록할 수 있습니다.
documentEvent.observe( new Observer<Document
>() { public void notify(Document doc) { ... } } );
이벤트 바인딩 유형은 인벤트 통지 삽입 지점에 의해 지정되거나 observe()
방식에 이벤트 바인딩 유형 인스턴스를 전달하여 지정할 수 있습니다:
documentEvent.observe( new Observer<Document
>() { public void notify(Document doc) { ... } },
new AnnotationLiteral<Updated
>(){} );
이벤트 바인딩 유형은 어노테이션 멤버를 갖을 수 있습니다:
@BindingType
@Target({PARAMETER, FIELD})
@Retention(RUNTIME)
public @interface Role {
RoleType value();
}
멤버 값은 옵저버에게 전달되는 메세지의 범위를 좁히는데 사용됩니다:
public void adminLoggedIn(@Observes @Role(ADMIN) LoggedIn event) { ... }
이벤트 바인딩 유형 멤버는 이벤트 통지 삽입 지점에 있는 어노테이션을 통해 이벤트 생산자에의해 정적으로 지정될 수 있습니다:
@Observable @Role(ADMIN) Event<LoggedIn
> LoggedInEvent;}}
다른 방법으로, 이벤트 바인딩 유형 멤버 값은 이벤트 생산자에 의해 동적으로 지정될 수 있습니다. AnnotationLiteral
의 추상화 하부클래스를 작성하여 시작합니다:
abstract class RoleBinding
extends AnnotationLiteral<Role
>
implements Role {}
이벤트 생산자는 이러한 클래스의 인스턴스를 fire()
로 전달합니다:
documentEvent.fire( document, new RoleBinding() { public void value() { return user.getRole(); } } );
이벤트 바인딩 유형은 통합할 수 있습니다, 예:
@Observable @Blog Event<Document
> blogEvent;
...
if (document.isBlog()) blogEvent.fire(document, new AnnotationLiteral<Updated
>(){});
이러한 이벤트가 발생하면, 다음과 같은 옵저버 방식 모두가 통지되게 됩니다:
public void afterBlogUpdate(@Observes @Updated @Blog Document document) { ... }
public void afterDocumentUpdate(@Observes @Updated Document document) { ... }
public void onAnyBlogEvent(@Observes @Blog Document document) { ... }
public void onAnyDocumentEvent(@Observes Document document) { ... }}}
트랜잭션 옵저버는 이벤크가 제기된 트랜잭션의 완료 단계 이전 또는 이후 동안 이벤트 통지를 받습니다. 예를 들어, 다음과 같은 옵저버 방식은 트랜잭션이 Category
크리를 성공적으로 업데이트했을 경우에만 애플리케이션 컨텍스트에서 캐시된 쿼리 결과 모음을 새로고침해야 합니다:
public void refreshCategoryTree(@AfterTransactionSuccess @Observes CategoryUpdateEvent event) { ... }
세 가지 종류의 트랜잭션 옵저버가 있습니다:
트랜잭션의 완료 단계 이후 동안 트랜잭션이 성공적으로 완료했을 경우에만, @AfterTransactionSuccess
옵저버를 호출합니다.
트랜잭션의 완료 단계 이후 동안 트랜잭션이 성공적으로 완료하지 않을 경우에만, @AfterTransactionFailure
옵저버를 호출합니다.
트랜잭션의 완료 단계 이후 동안 @AfterTransactionCompletion
옵저버를 호출합니다
트랜잭션의 완료 단계 이전 동안 @BeforeTransactionCompletion
옵저버를 호출합니다
트랜잭션 옵저버는 Web Beans와 같은 상태 유지 객체 모델에서 아주 중요합니다. 이는 단일 원자성 트랜잭션보다 오래 지속되기 때문입니다.
애플리케이션 범위에 있는 JPA 쿼리 결과 모음을 캐시했다고 가정합니다:
@ApplicationScoped @Singleton
public class Catalog {
@PersistenceContext EntityManager em;
List<Product
> products;
@Produces @Catalog
List<Product
> getCatalog() {
if (products==null) {
products = em.createQuery("select p from Product p where p.deleted = false")
.getResultList();
}
return products;
}
}
때때로 Product
는 생성 또는 삭제됩니다. 이러한 상황이 발생하면, Product
카탈로그를 새로고침해야 합니다. 하지만, 이러한 새로 고침을 실행하기 전 트랜젝션이 성공적으로 완료할 때 까지 기다리셔야 합니다!
Product
를 생성 및 삭제하는 Web Bean은 이벤트를 제기할 수 있습니다, 예:
@Stateless
public class ProductManager {
@PersistenceContext EntityManager em;
@Observable Event<Product
> productEvent;
public void delete(Product product) {
em.delete(product);
productEvent.fire(product, new AnnotationLiteral<Deleted
>(){});
}
public void persist(Product product) {
em.persist(product);
productEvent.fire(product, new AnnotationLiteral<Created
>(){});
}
...
}
Catalog
는 트랜잭션을 성공적으로 완료한 후에 이벤트를 옵저버할 수 있습니다:
@ApplicationScoped @Singleton
public class Catalog {
...
void addProduct(@AfterTransactionSuccess @Observes @Created Product product) {
products.add(product);
}
void addProduct(@AfterTransactionSuccess @Observes @Deleted Product product) {
products.remove(product);
}
}