[weld] 对事不对bean
wuhaixing
2010-04-29
DI实现松散耦合的方式是让注入的bean类型可变,部署时或运行时都行。事件机制更进一步,根本就不需要知道交互双方的bean类型。container把生事者搞出来的事件交给事件的关注者。
听起来像observer/observable模式?不过有点不一样的地方:
CDI的事件通告机制也多多少少的使用了我们在DI服务中见过的typesafe方式。 |
|
wuhaixing
2010-04-29
事件对象
事件的关注者需要了解事件的相关信息,生事者负责将信息封装进事件对象中传递给关注者。对事件对象的唯一要求就是他不能为可变类型的实例。事件还可以带有qualifiers,以便于关注者区分同一类型的不同事件。qualifier在这里的作用和topic选择器类似,让selector可以缩小其所关注的事件集。 事件的qualifier就是普通的qualifier,下面是一个例子: @Qualifier @Target({FIELD, PARAMETER}) @Retention(RUNTIME) public @interface Updated {} |
|
wuhaixing
2010-04-29
关注者
带有标记为@Observes参数的方法就是observer方法。 public void onAnyDocumentEvent(@Observes Document document) { ... } 被标记的参数称为事件参数。事件参数的类型就是所关注的事件类型,这里就是Document。事件参数也可以带有qualifier。 public void afterDocumentUpdate(@Observes @Updated Document document) { ... } observe方法不是必须带有event qualifier的--这种情况下所有该类型的事件都会受到关注。如果带有qualifier标记,只有带有相同标记的事件才会被关注。 observe方法也可以带有其他参数,这些参数会由container注入: public void afterDocumentUpdate(@Observes @Updated Document document, User user) { ... } |
|
wuhaixing
2010-04-29
生事者
生事者需要一个Event接口的参数化类型实例来发起事件,这个由container负责提供: @Inject @Any Event<Document> documentEvent; 调用fire()方法就可以发起事件了,fire()方法中得带这事件对象作为参数: documentEvent.fire(document); 所有符合下列条件的关注者方法都会看到这个事件:
container仅仅是负责将event对象赋值给event参数,并调用ovserver方法们。如果observer方法出现了异常,container停止调用,并且异常会由fire()方法向上传递。 在event上用qualifier有两种方式:
第一种超级简单: @Inject @Updated Event<Document> documentUpdatedEvent; 这个Event实例fired的事件,都会带上@Updated。收到这个事件的关注者方法要:
但这种方式不太灵活,CDI也允许我们通过创建 AnnotationLiteral类的子类来动态的生成qualifier,并把这个qualifier放到Event的select()方法中。 documentEvent.select(new AnnotationLiteral<Updated>(){}).fire(document); qualifier可以有多个,标记在Event注入点和传递到select()方法中的是集成到一起的。 |
|
wuhaixing
2010-04-29
我有个条件
通常情况下,如果当前的context中没有observer的实例,container会创建一个observer的实例然后把event交给他。有时候你可能不想让container这么干,只想在当前context中已经存在observer实例的时候才递交event。 给@Observer标记加上receive = IF_EXISTS,就得到了一个conditional observer。 public void refreshOnDocumentUpdate(@Observes(receive = IF_EXISTS) @Updated Document d) { ... } 这个不能用在@Dependent scope中的bean上,因为那样他就永远都不会被调用了。 |
|
wuhaixing
2010-04-29
带member的事件qualifier
事件qualifier 也可以带annotation members: @Qualifier @Target({PARAMETER, FIELD}) @Retention(RUNTIME) public @interface Role { RoleType value(); } member的值用来进一步明确传递给observer的消息: public void adminLoggedIn(@Observes @Role(ADMIN) LoggedIn event) { ... } qualifier的 members可以在event notifier注入点静态标明: @Inject @Role(ADMIN) Event<LoggedIn> loggedInEvent; 也可以在select方法中动态说明。 先定义一个 AnnotationLiteral的抽象类: abstract class RoleBinding extends AnnotationLiteral<Role> implements Role {} 然后在select()中: documentEvent.select(new RoleBinding() { public void value() { return user.getRole(); } }).fire(document); |
|
wuhaixing
2010-04-29
多个qualifier
事件的qualifier可以组合: @Inject @Blog Event<Document> blogEvent; ... if (document.isBlog()) blogEvent.select(new AnnotationLiteral<Updated>(){}).fire(document); 当这个事件被触发后,下面的observer方法都能收到: 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) { ... }}} |
|
wuhaixing
2010-04-29
事务化的关注者
事务化的关注者会在 发起事件的 事务结束阶段 之前(或之后)收到事件通知。下面这个observer方法负责刷新application context中的查询结果, 但他只在Category tree更新事务成功之后才会执行: public void refreshCategoryTree(@Observes(during = AFTER_SUCCESS) CategoryUpdateEvent event) { ... } weld支持5种 transactional observers:
事务化的observer在statuful对象模型中非常重要,因为对象状态的持续时间通常都会比一个atomic transaction要长。 看看下面这个情况,application scope中缓存了一个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目录。但是得等增删事务成功结束之后。 负责增删的方法可以发起事件: @Stateless public class ProductManager { @PersistenceContext EntityManager em; @Inject @Any Event<Product> productEvent; public void delete(Product product) { em.delete(product); productEvent.select(new AnnotationLiteral<Deleted>(){}).fire(product); } public void persist(Product product) { em.persist(product); productEvent.select(new AnnotationLiteral<Created>(){}).fire(product); } ... } Catalog关注相应的事件,就可以在事务成功结束之后进行相应的操作了: @ApplicationScoped @Singleton public class Catalog { ... void addProduct(@Observes(during = AFTER_SUCCESS) @Created Product product) { products.add(product); } void addProduct(@Observes(during = AFTER_SUCCESS) @Deleted Product product) { products.remove(product); } } |