博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
CDI services--Event(事件)
阅读量:5823 次
发布时间:2019-06-18

本文共 7348 字,大约阅读时间需要 24 分钟。

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.

转载于:https://www.cnblogs.com/formyfish/p/10752017.html

你可能感兴趣的文章
mysql-This version of MySQL doesn’t yet support ‘LIMIT & IN/ALL/ANY/SOME 错误解决
查看>>
BIEE Demo(RPD创建 + 分析 +仪表盘 )
查看>>
Cocos2dx 3.0开发环境的搭建--Eclipse建立在Android工程
查看>>
基本概念复习
查看>>
重构第10天:提取方法(Extract Method)
查看>>
Android Fragment使用(四) Toolbar使用及Fragment中的Toolbar处理
查看>>
解决pycharm在ubuntu下搜狗输入法一直固定在左下角的问题
查看>>
多线程day01
查看>>
react-native 模仿原生 实现下拉刷新/上拉加载更多(RefreshListView)
查看>>
MySQL出现Access denied for user ‘root’@’localhost’ (using password:YES)
查看>>
通过Roslyn构建自己的C#脚本(更新版)(转)
查看>>
红黑树
查看>>
UIImagePickerController拍照与摄像
查看>>
python调用windows api
查看>>
第四章 mybatis批量insert
查看>>
Java并发框架——什么是AQS框架
查看>>
【数据库】
查看>>
Win配置Apache+mod_wsgi+django环境+域名
查看>>
linux清除文件内容
查看>>
WindowManager.LayoutParams 详解
查看>>