Cdi中的event事件,是整个CDI的精华所在之一.其有点类似设计模式中的观察者模式.但也有不同的地方.如下3点:

  1. 不仅是生产者(producers)从观察者(observers)解耦.观察者也从生产者解耦.
  2. 观察者可以指定“选择器”的组合来缩小的事件通知
  3. 观察者可以立即通知,或者可以指定交付的事件应该推迟到当前事务的结束。

即用一种维护生产者和观察者之间的分离代码的方式,来产生和订阅(即观察)在应用程序中发生的事件。使用 javax.enterprise.event.Event 类创建事件,并使用 CDI 的 @Observes 标注订阅处理事件。

1. Event payload(事件的有效载入)

事件对象只不过是一个具体的Java类的实例。
一个事件可指定限定符,观察者可以区别于其他相同类型的事件。
限定符的功能很像主题选择器, 允许限定符决定观察器将观察哪些事件。
使用@ qualifier定义的一个例子:

1
2
3
4
@Qualifier
@Target({METHOD, FIELD, PARAMETER, TYPE})
@Retention(RUNTIME)
public @interface Updated {}

另外,事件的创建和订阅是类型安全的.

2. Event observers(event的观察者)

一个观察者的处理方式是在方法中,加入一个参数注解@Observes.如下所示:

1
2
public void onAnyDocumentEvent(@Observes Document document)
{ ... }

带注解的参数称为事件参数。事件的参数类型是观察到的事件类型。事件参数还可以指定限定符。如下:

1
public void afterDocumentUpdate(@Observes @Updated Document document) { ... }

当然也可以有其他参数

1
public void afterDocumentUpdate(@Observes @Updated Document document, User user) { ... }

3. Event producers(event生产者)

Event producers的fire事件是使用参数化Event interface的实例.如下,通过@Inject注入该接口的一个实例.

1
@Inject @Any Event<Document> documentEvent;

而事件生产者通过调用fire()方法,并传递"事件对象"从而激活事件处理.

1
documentEvent.fire(document);

通过事件对象的参数值,容器调用所有观察者的方法,如果任何观察者方法抛出一个异常,容器会停止调用观察者方法,异常将会由fire()方法抛出。 
Qualifiers 在事件中应用方式有两种:

在event的注入点(Injection point)通过注解

1
@Inject @Updated Event<Document> documentUpdatedEvent;

  在观察者的处理方法中,通过事件对Qualifiers的选择.

1
public void afterDocumentUpdate(@Observes @Updated Document document) { ... }

注解注入的缺点是,我们不能动态地指定限定符。
CDI也考虑到了这一点.

4.AnnotationLiteral动态注入对应事件

1
documentEvent.select(new AnnotationLiteral<Updated>(){}).fire(document);

documentEvent注入点不用再使用限定符 @Updated. 这样可以在程序中判断后进行分支处理.

1
2
3
4
5
if(num==1){
documentEvent.select(new AnnotationLiteral<Updated>(){}).fire(document);
}else{
documentEvent.select(new AnnotationLiteral<Other>(){}).fire(document);
}

事件可以有多个事件限定符,通过select()方法可以使用任意的注解组合在事件注入点和限定符实例上.

5.Conditional observer methods

默认情况下,在当前上下文如果没有一个观察者的实例,容器将为事件实例化观察者.
但我们希望传递给观察者的实例是已经存在于上下文中的观察者.
指定一个有条件的观察者的方式是在@Observes注释上添加receive = IF_EXISTS

1
public void refreshOnDocumentUpdate(@Observes(receive = IF_EXISTS) @Updated Document d) { ... }

Note 
A bean with scope @Dependent cannot be a conditional observer, since it would never be called!

6.Event qualifiers with members

1
2
3
4
5
6
7
@Qualifier
@Target({METHOD, FIELD, PARAMETER, TYPE})
@Retention(RUNTIME)
public @interface Role {
 
   RoleType value();
}

可以通过注解的value值传递信息给observer.

1
public void adminLoggedIn(@Observes @Role(ADMIN) LoggedIn event) { ... }

在事件注入点的使用

1
@Inject @Role(ADMIN) Event<LoggedIn> loggedInEvent;

在AnnotationLiteral方式中的使用: 
先定义一个AnnotationLiteral的抽象类

1
abstract class RoleBinding extends AnnotationLiteral<Role> implements Role {}

通过select()方法的使用代码

1
2
3
documentEvent.select(
    new RoleBinding() {public void value() { return user.getRole(); }}
    ).fire(document);

7.Multiple event qualifiers

qualifiers 是可以多重组合的.如下代码:

1
2
3
@Inject @Blog Event<Document> blogEvent;
...
if (document.isBlog()) blogEvent.select(new AnnotationLiteral<Updated>(){}).fire(document);

下面所有这些观察方法将得到通知。

1
2
3
4
5
6
7
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) { ... }}}

然而,如果还有一个观察者的方法:

1
public void afterPersonalBlogUpdate(@Observes @Updated @Personal @Blog Document document) { ... }

它不会通知,因为@Personal并未包含在事件发生处.

8.事务性处理的transactional observers

事务处理的observers 在事务完成之前或之后的阶段才会收到事件通知.
例如,下面的观察方法需要在应用程序上下文中刷新一个查询的结果集,但是只有在 Category 更新成功才会执行:

1
public void refreshCategoryTree(@Observes(during = AFTER_SUCCESS) CategoryUpdateEvent event) { ... }

一共有五种transactional observers:

IN_PROGRESS       --- observers被立即通知  (default)

AFTER_SUCCESS     --- 在事务成功完成后,observers会被通知.

AFTER_FAILURE     --- 在事务完成失败后,observers会被通知.

AFTER_COMPLETION  --- observers在交易完成后的阶段被调用

BEFORE_COMPLETION --- observers在交易完成前阶段被调用

在一个有状态的对象模型(stateful object model)中,Transactional observers是非常重要的.因为那些状态经常是长事务的.
想象一下,我们已经在application scope范围缓存一个JPA查询,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import javax.ejb.Singleton;
import javax.enterprise.inject.Produces;
 
@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;
   }
}

如果一个产品被创建或删除,我们需要重新整理产品目录,这个时候我们必须要等到这个更新的事务成功完成后.

创建和删除产品的Bean可以引发事件,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import javax.enterprise.event.Event;
 
@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);
   }
   ...
}

在事务完成后,对产品目录用观察者的方法进行更新/删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import javax.ejb.Singleton;
 
@ApplicationScoped @Singleton
public class Catalog {
 
   ...
 
   void addProduct(@Observes(during = AFTER_SUCCESS) @Created Product product) {
      products.add(product);
   }
 
   void removeProduct(@Observes(during = AFTER_SUCCESS) @Deleted Product product) {
      products.remove(product);
   }
}

DEMO

概述流程:

A: event 主体

首先是2个事件.  1.run,跑 事件   2.walk,走 事件 
页面触发这2个事件.首先在后台定义@Qualifier,对应每个事件. 在CDI中所有的对象和生产者都是限定类型的.所以需要指定具体的@Qualifier.而这里事件就2个,所以如下:

run.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
 
import javax.inject.Qualifier;
 
@Qualifier
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RUNTIME)
@Documented
public @interface Run {
 
}

walk.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@Qualifier
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RUNTIME)
@Documented
public @interface Walk {
 
}

定义好后,我们需要定义具体的事件处理的主题.也就是运动.不管是跑还是走,都是运动的一种.所以定义运动事件主体.

其实主要是因为在这里是自己想的一个CDI EVENT的场景,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.util.Date;
 
public class ExerciseEvent {
    private String type;  //walk or run
    private Long howfar;
    private Date datetime;
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public Long getHowfar() {
        return howfar;
    }
    public void setHowfar(Long howfar) {
        this.howfar = howfar;
    }
    public Date getDatetime() {
        return datetime;
    }
    public void setDatetime(Date datetime) {
        this.datetime = datetime;
    }
     
    @Override
    public String toString() {
        return "在"+this.datetime+",你"+this.type+"--"+(this.howfar.toString());
    }
}

不忙处理页面,这个时候,我们应该对走还是跑做具体的处理.

分析一下,cdi的event处理,主要是2个,一个ob一个producer.现在,我们已经定义好了event.那么接着就是先处理observer.

CDI services--Event(事件)的更多相关文章

  1. CDI(Weld)高级<4> Event(事件) (转)

    目录[-] 1. Event payload(事件的有效载入) 2. Event observers(event的观察者) 3. Event producers(event生产者) 4.Annotat ...

  2. CDI Services *Decoretions *Intercepters * Scope * EL\(Sp EL) *Eventmodel

    1.Decorators装饰器综述 拦截器是一种强大的方法在应用程序捕捉运行方法和解耦.拦截器可以拦截任何java类型的调用.  这使得拦截器适合解决事务管理,安全性,以及日记记录.  本质上说,拦截 ...

  3. [.NET] C# 知识回顾 - Event 事件

    C# 知识回顾 - Event 事件 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/6060297.html 序 昨天,通过<C# 知识回顾 - ...

  4. Event事件

    妙味课堂-Event事件 1.焦点:当一个元素有焦点的时候,那么他就可以接受用户的输入(不是所有元素都能接受焦点) 给元素设置焦点的方式: 1.点击 2.tab 3.js 2.(例子:输入框提示文字) ...

  5. JS学习笔记9之event事件及其他事件

    -->鼠标事件-->event事件对象-->默认事件-->键盘事件(keyCode)-->拖拽效果 一.鼠标事件 onclick ---------------鼠标点击事 ...

  6. JS(event事件)

    常用的event事件: 属性 此事件发生在何时... onabort 图像的加载被中断. onblur 元素失去焦点. onchange 域的内容被改变. onclick 当用户点击某个对象时调用的事 ...

  7. event事件学习小节

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. Javascript 事件对象(二)event事件

    Event事件: <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" ...

  9. js event 事件兼容浏览器 ie不需要 event参数 firefox 需要

    js event 事件兼容浏览器    ie不需要 event参数   firefox 需要 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 ...

随机推荐

  1. [八省联考2018]林克卡特树lct

    题解: zhcs的那个题基本上就是抄这个题的,不过背包的分数变成了70分.. 不过得分开来写..因为两个数组不能同时满足 背包的话就是 $f[i][j][0/1]$表示考虑i子树,取j条链,能不能向上 ...

  2. 03.DataStructure

    01.list ''' list 특징 - 1차원 배열 구조 형식) 변수 = [값1, 값2] - 다양한 자료형 저장 가능 - index 사용=순서 존재 형식) 변수[n] - 값 수정( ...

  3. pc端网页,移动端网页(andorid、ios)离开页面做一个时间存储

    如图所示:在一个页面中做了一个倒计时,然后用户想离开页面做其他事情,需求是离开页面之后把时间保存,下一次进来继续的时候时间还是上次离开的时间 第一次我用的事件是:  // window.onbefor ...

  4. Windows Server 2008 R2 修改远程桌面服务RDP默认端口及相应的防火墙配置

    修改以下两个注册表项当中的默认端口3389为自定义端口: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\Wd ...

  5. 20172328 2018—2019《Java软件结构与数据结构》第二周学习总结

    20172328 2018-2019<Java软件结构与数据结构>第二周学习总结 概述 Generalization 本周学习了第三章集合概述--栈和第四章链式结构--栈.主要讨论了集合以 ...

  6. java线程系列之三(线程协作)

    本文来自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/7433673,转载请注明. 上一篇讲述了线程的互斥(同步),但是在很多情况 ...

  7. MVC 程序在编译时提示 GAC与 Temporary ASP.NET Files目录内引用文件版本不一致

    今天在调试Mvc程序时,提示GAC与Temporary ASP.NET Files目录内引用文件版本不一致. [A]System.Web.WebPages.Razor.Configuration.Ho ...

  8. 关于Android文件Apk下载的那点事

    1.Android文件Apk下载变ZIP压缩包解决方案 如果你的下载服务器为Nginx服务器,那么,在Nginx安装目录下的conf/mime.types文件的对应位置,加上以下一行语句,指定APK文 ...

  9. mobile_视口

    document.documentElement.clientWidth       不包含滚动条 window.innerWidth                                  ...

  10. Snackbar 提醒

    类似于Toast,属于design包,不要忘记导入design package com.xiaozhuyisheng.jinjiedemos.activity; import android.supp ...