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

目录[-]

以前发过一个粗略篇,已经删除.这次重新修订.

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 在事件中应用方式有两种:

注解注入的缺点是,我们不能动态地指定限定符。
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:

  1. IN_PROGRESS       --- observers被立即通知  (default)
  2. AFTER_SUCCESS     --- 在事务成功完成后,observers会被通知.
  3. AFTER_FAILURE     --- 在事务完成失败后,observers会被通知.
  4. AFTER_COMPLETION  --- observers在交易完成后的阶段被调用
  5. 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

概述流程:

我在这里也是实际阐释一下.毕竟国内CDI方面的东西基本没有,也给学习CDI的朋友一个参考.

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.

B: Ob 定义

?


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

public class ExerciseHandler implements Serializable{

    private static final long serialVersionUID = 3245934049396896828L;

    

    @Inject

    private Logger log;

    

    List<ExerciseEvent> exercise=new ArrayList<ExerciseEvent>();

    

    public void run(@Observes @Run ExerciseEvent runEvent){

        log.info("CDI---run方法!");

        this.exercise.add(runEvent);

    }

    

    public void walk(@Observes @Walk ExerciseEvent walkEvent){

        log.info("CDI---walk方法!");

        this.exercise.add(walkEvent);

    }

    

    @Produces

    @Named

    public List<ExerciseEvent> getExercise() {

        return exercise;

    }

    

}

相关语法说明,如果看了上面翻译自jboss的文档的说明外,应该也就明白这么的代码意思.

这个类的run方法将会在系统观察到有地方触发了限定符为@run,并且事件是ExerciseEvent的方法.就会去执行这个方法.\

C: producer 定义

这里就要继续写producer的相关类了.本例大家可以知道,是由页面触发的相关Exercise事件.run or walk.先是一个页面.

JSF页面,大家可以看到h:dateTable.在看看上面的 @Produces注解.就上面OB定义里的最后一段代码.

?


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

31

32

33

34

35

36

37

<?xml version="1.0" encoding="UTF-8"?>

<ui:composition xmlns="http://www.w3.org/1999/xhtml"

    xmlns:ui="http://java.sun.com/jsf/facelets"

    xmlns:h="http://java.sun.com/jsf/html"

    xmlns:f="http://java.sun.com/jsf/core"

    template="/WEB-INF/templates/template.xhtml">

    

        <ui:define name="content">

        <h:form>

            <h:outputLabel value="Far:" />

            <h:inputText value="#{exerciseBean.far}" />

            <h:selectOneRadio value="#{exerciseBean.type}" required="true">

                <f:selectItem itemLabel="Run" itemValue="run" />

                <f:selectItem itemLabel="Walk" itemValue="walk" />

            </h:selectOneRadio>

            <h:commandButton value="Go!!!" action="#{exerciseBean.process()}" />

        </h:form>

        <h:dataTable var="exercise" value="#{exercise}" styleClass="zebra-striped">

            <h:column>

                <f:facet name="header">Date</f:facet>

                <h:outputText value="#{exercise.datetime}">

                    <f:convertDateTime type="date" pattern="yyyy/MM/dd" />

                </h:outputText>

            </h:column>

            <h:column>

                <f:facet name="header">type</f:facet>

                <h:outputText value="#{exercise.type}" />

            </h:column>

            <h:column>

                <f:facet name="header">howfar</f:facet>

                <h:outputText value="#{exercise.howfar}" />

            </h:column>

        </h:dataTable>

    </ui:define>

</ui:composition>

对应的backingBean代码:

?


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

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

@Named

@SessionScoped

public class ExerciseBean implements Serializable{

    private static final long serialVersionUID = -2164098635097534027L;

    

    @Inject private Logger log;

    

    @Inject

    @Run

    Event<ExerciseEvent> runEventProducer;

    

    @Inject

    @Walk

    Event<ExerciseEvent> walkEventProducer;

    

    private String type="run";

    private Long far;

    private ExerciseEvent event=new ExerciseEvent();

    

    public void process(){

        event.setType(this.type);

        event.setHowfar(far);

        event.setDatetime(new Date());

        if(this.event.getType().equals("run")){

            log.info("Run--Fire");

            this.runEventProducer.fire(event);

        }else{

            log.info("Walk--Fire");

            this.walkEventProducer.fire(event);

        }

    }

    public ExerciseEvent getEvent() {

        return event;

    }

    public void setEvent(ExerciseEvent event) {

        this.event = event;

    }

    public Event<ExerciseEvent> getRunEventProducer() {

        return runEventProducer;

    }

    public void setRunEventProducer(Event<ExerciseEvent> runEventProducer) {

        this.runEventProducer = runEventProducer;

    }

    public Event<ExerciseEvent> getWalkEventProducer() {

        return walkEventProducer;

    }

    public void setWalkEventProducer(Event<ExerciseEvent> walkEventProducer) {

        this.walkEventProducer = walkEventProducer;

    }

    public String getType() {

        return type;

    }

    public void setType(String type) {

        this.type = type;

    }

    public Long getFar() {

        return far;

    }

    public void setFar(Long far) {

        this.far = far;

    }

}

启动页面后,点击按钮,选择不同的
radio方式
,run或者walk,我就不截图了.

发一点后台的输出:

16:33:01,899 INFO  (http-/0.0.0.0:8080-1) Walk--Fire
16:33:01,901 INFO  (http-/0.0.0.0:8080-1) CDI---walk方法!
16:51:55,712 INFO  (http-/0.0.0.0:8080-1) Walk--Fire
16:51:55,713 INFO  (http-/0.0.0.0:8080-1) CDI---walk方法!
16:52:54,044 INFO  (http-/0.0.0.0:8080-1) Run--Fire
16:52:54,045 INFO   (http-/0.0.0.0:8080-1) CDI---run方法!

原文:http://my.oschina.net/zhaoqian/blog/265207#OSC_h1_7

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

时间: 2024-10-12 02:52:04

CDI(Weld)高级<4> Event(事件) (转)的相关文章

Event事件详解

首先提到event,先要明白event的产生,也要先明白焦点,什么是焦点.焦点 : 使浏览器能够区分用户输入的对象,当一个元素有焦点的时候,那么他就可以接收用户的输入. 我们可以通过一些方式给元素设置焦点.而并不是所有元素都能够接收焦点的,能够响应用户操作的元素才有焦点.例如: <input type="text" /> <a href="http://www.baidu.com">百度</a> 说到input,平时用的较多的是o

event事件学习小节

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>event事件</title> </head> <body> <script> document.onclick=function(ev){//谷歌火狐的写法,IE9以上支持,往下不支持: var e=ev; consol

trigger()的event事件对象之坑

问题引入,先贴一段有问题的代码,如果你对 trigger()  这个函数了解不透彻,还真看不出这段代码错在哪.完成的功能是样式转换器,想让页面在加载后自行触发点击事件隐藏三个按钮,但是效果如图并没有隐藏按钮们:控制台会报错: target  属性是 undefined ,肯定是 event 这个事件对象没获取到的问题,程序到17行出错停止,这让我感到疑问:自定义触发的事件难道没有event对象吗?. 1 <div id="switcher" class="switche

笔记-【3】-event事件对象的详解!

event事件对象:是指当前对象发生的事件的一些详细的信息在event这个对象里. event对象从哪里来?从事件函数中传入 obj. //e就会当前的事件对象event } 对象就有属性和方法:那么event对象也有属性和方法 event的属性和方法: { 属性: button :  当前事件的方法中判断鼠标的按键位置 有三个值: 0 (左键) 1(滚轮) 2(右键) ctrlkey:  判断是否按下了ctrl键; altkey:  判断是否按下了alt键; shiftkey:  判断是否按下

JS之Event事件

<script> function ff(event) { var e = window.event||event;//Event对象:发生当前事件的状态 // var src = e.srcElement||e.target;//获得事件中的value值 var c = e.keycode||e.charCode;//获得键盘输入的值的编码 // alert(e); // alert(src.value); alert(c); } /** var e; if(window.event){ e

HTML DOM event 事件 (丫头, 好想你了)

HTML DOM event 事件, 用用就知道了 function isKeyPressed(eventssdasd)     // 这个地方其实无关紧要,就是定义一个名称而已, 不过为了好看, 最好还是写成 event{  if (event.altKey==1)                        // event代表事件的状态,例如触发event对象的元素.鼠标的位置及状态.按下的键等等,此处为检测 alt 键是否按下    {  // 此处的 event 必须写正确, 你可以

简单总结焦点事件、Event事件对象、冒泡事件

每学习一些新的东西,要学会复习,总结和记录. 今天来简单总结一下学到的几个事件:焦点事件.Event事件对象.冒泡事件 其实这几个事件应该往深的说是挺难的,但今天主要是以一个小菜的角度去尝试理解一些基本的知识点. 1.焦点事件: 1.1概念理解: 想象场景:当一堆text文本框出现在面前,当点击其中一个文本框,它就会响应用户,并出现光标闪动(这时,点击令它获得焦点). 所以说:焦点事件是用来让浏览器区分哪一个对象是用户要进行操作(输入值.选择.跳转)的. 总结===>  浏览器(区分)哪一个对象

13 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件  queue队列 生产者消费者模型 Queue队列 开发一个线程池

本节内容 操作系统发展史介绍 进程.与线程区别 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者消费者模型 Queue队列 开发一个线程池 进程 语法 进程间通讯 进程池 操作系统发展史 手工操作(无操作系统) 1946年第一台计算机诞生--20世纪50年代中期,还未出现操作系统,计算机工作采用手工操作方式. 手工操作程序员将对应于程序和数据的已穿孔的纸带(或卡片)装入输入机,然后启动输入机把

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

-->鼠标事件-->event事件对象-->默认事件-->键盘事件(keyCode)-->拖拽效果 一.鼠标事件 onclick ---------------鼠标点击事件oncontextmenu------鼠标右键点击onmouseover --------鼠标移上onmouseout ---------鼠标移出onmousedown -------鼠标按下onmousemove -------鼠标移动onmouseup ----------鼠标抬起 1 <head