服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > JAVA > 新手入门 > 基础入门 > 查看文档

在ejb环境中实现“观察者”模式


  在ejb环境中实现“观察者”模式

observer模式(“观察者”模式)或许是降低对象结合程度的最佳方法之一。例如,在编写一个典型的应用程序时,你可能决定提供一个工厂或管理器触发适当的事件,以这些事件的一组监听器的形式提供分离的业务逻辑;此后,系统的启动类就在工厂或者管理器创建完毕之后,把这些监听器关联到工厂或者管理器。

在大多数j2ee系统中,这种工厂/管理器都是无状态的会话bean。ejb容器处理对无状态会话bean的请求,根据请求创建无状态会话bean的实例,或重用现有的实例。问题在于,每次初始化一个新的bean实例时都必须伴有一组监听器,这组监听器和为其他实例而运行的监听器完全相同。合理的方案应该是,当一个无状态会话bean实例被创建的时候,它访问某个知识库,通过一定的方法获知相关的监听器,然后建立和这些监听器的关系。在这篇文章中,我要介绍的就是如何实现这一方案。

一种典型的情形
请考虑下面这种典型的情形。一个在线拍卖系统有一个无状态会话bean,名为auctionfactory,这个bean创建拍卖(auction)对象。对于每一个新创建的拍卖对象,业务逻辑要求系统执行一些附加的操作,比如发送email、更新用户摘要文件,等等。在许多系统上,创建拍卖对象和执行这些附加操作的代码如下所示:

public auction createauction(int numofcontainers) throws remoteexception{ someauctionclass auction = new someauctionclass (numofcontainers); // 创建拍卖对象之后,接下来要编写下面这种执行附加操作的代码 //(而不是简单地发送一个“拍卖对象已经创建”的事件) sendemailsaboutnewauction(auction); updateuserprofiles(auction); doothernotificationstuffaboutnewauction(auction); //等等.... return auction;}


之所以要编写这种质量很差的代码,原因就在于初始化各个bean实例时附带一组必需的监听器很困难。如果这个bean是一个事件发布者,而且每一个bean实例初始化的时候都带有一组它需要的监听器,上述代码可以变得更简洁、更强壮,例如:

public auction createauction(int numofcontainers) throws remoteexception{ someauctionclass auction = new someauctionclass (numofcontainers); fireauctioncreated(auction); return auction;}


基本原理说明
实现本文技巧的基本原理其实很简单。一个listenerregistry类实现事件发布者类和必须关联到该类的监听器之间的映射。系统的启动模块初始化listenerregistry,为每一种发布者类型初始化一组必需的监听器。当发布者被创建或激活,它就访问listenerregistry,把它的类传递给listenerregistry,获得一组监听器。然后,发布者把所有这些监听器关联到自身。就这么简单。


你也许会很自然地问,“什么是listenersupplier?”和“为什么不直接注册和使用eventlistener?”确实可以;事实上,该框架的第一个版本就是直接使用事件监听器。但是,如果在listenerregistry中使用监听器,这些监听器必须在注册的时候就存在。另一方面,如果注册的是一个“中介者”listenersupplier(监听器提供者),你就可以自由地把创建/提取监听器延迟到它绝对必需的时候。listenersupplier类似于工厂,但两者的不同之处在于,listenersupplier并非必定要创建新的监听器,它的目标是返回监听器。每次getlistener()方法被调用时,listenersupplier是创建一个新的监听器,还是每次都返回同一实例,这一切由开发者自己决定。

因此,结合运用listenerregistry和监听器提供者,我们可以在事件发布者和观察者(或监听器)不存在的情况下,建立两者之间的关系。可以认为,这个优点很重要,它延迟了发布者和观察者的实例化。

具体实现
在这一部分,你将看到整个框架中所有组成部分的实现代码。我假定你已经了解必要的基础知识,比如ejb、同步,当然还有java核心库。完整的源代码可以从本文最后下载。

下面是listenerregistry接口的代码:

//listenerregistry.javapackage com.jwasp.listener;import java.util.eventlistener;import java.rmi.remoteexception;import com.jwasp.listener.listenersupplier;/*** 框架的核心。实现事件发布者类和监听器提供者之间的映射*/public interface listenerregistry {void addlistenersupplier(listenersupplier listenersupplier, class publisherclass);void removelistenersupplier(listenersupplier listenersupplier, class publisherclass);eventlistener[] getlisteners(class publisherclass) throws remoteexception, listeneractivationexception;}


下面是listenersupplier接口:

//listenersupplier.javapackage com.jwasp.listener;
import java.util.eventlistener;
/**
* 为方便起见而提供的“中介者”,负责创建/提取相应的监听器
*/
public interface listenersupplier {
/**
* 返回和指定发布者类相对应的监听器
*/
eventlistener getlistener(class publisherclass)
throws java.rmi.remoteexception, listeneractivationexception;
}


下面是listenerregistry的缺省实现:

//defaultlistenerregistry.javapackage com.jwasp.listener;
import java.util.*;
import java.rmi.remoteexception;
import com.jwasp.listener.listenerregistry;
import com.jwasp.listener.listenersupplier;

/**
* listenerregistry的基本实现。该类是一个singleton(singleton模
* 式的主要作用是保证在java应用程序中,一个class只有一个实
* 例存在)。
* 当发布者请求监听器时,这个注册器返回的不仅有显式为
* 指定发布者类所注册的监听器,而且还有为发布者所有父类
* 注册的监听器。例如:
* 如果发布者b从发布者a扩展,而且已经有为a注册的监听
* 器提供者,那么,如果你把b类作为参数传递给getlisteners方
* 法,你得到的不仅有显式为b注册的监听器,还有所有为b类的
* 父类(在本例中,它是a)所注册的监听器。
*/
public class defaultlistenerregistry implements listenerregistry{
private defaultlistenerregistry(){}
public static defaultlistenerregistry getinstance(){
return instance;
}

public synchronized void addlistenersupplier(listenersupplier listenersupplier,
class publisherclass) {
assertnotnull("publisher class is null", publisherclass);
assertnotnull("listenersupplierr is null", listenersupplier);
collection listenersuppliers = (collection)mylistenersuppliersmap.get(publisherclass);
if ( listenersuppliers == null ) {
listenersuppliers = new arraylist();
mylistenersuppliersmap.put(publisherclass, listenersuppliers);
}
listenersuppliers.add(listenersupplier);
}

public synchronized void removelistenersupplier(listenersupplier listenersupplier,
class publisherclass) {
assertnotnull("publisher class is null", publisherclass);
assertnotnull("listenersupplierr is null", listenersupplier);
collection listenersuppliers = (collection)mylistenersuppliersmap.get(publisherclass);
if ( listenersuppliers == null ) {
return;
}
listenersuppliers.remove(listenersupplier);
if ( listenersuppliers.isempty() ) {
mylistenersuppliersmap.remove(publisherclass);
}
}

/**
* 返回一个为指定发布者类注册的eventlistener的数组。如果注册
* 器包含为该发布者注册的监听器提供者,它将依次访问每一个提供
* 者,调用其listenersupplier.getlistener(publisherclass)方法。

* @param publisherclass发布者类
* @返回eventlistener的数组
*/
public eventlistener[] getlisteners(class publisherclass)
throws remoteexception,listeneractivationexception {
//如最后一个参数设置成false,则禁止继承检查
collection listenersuppliers = getlistenersupplierscopy(publisherclass, true);
eventlistener[] array = new eventlistener[listenersuppliers.size()];
iterator i = listenersuppliers.iterator();
int count = 0;
while (i.hasnext()){
listenersupplier listenersupplier = (listenersupplier)i.next();
array[count] = listenersupplier.getlistener(publisherclass);
count++;
}
return array;
}

/**
* 返回当前已经为指定发布者类注册的监听器提供者副本。
* 这是一个同步方法,从而允许getlisteners方法保持非同
* 步。
* @param publisherclass
* @param checkinheritance 如为true,则返回为指定发布者类和它的所有父类
*

扫描关注微信公众号