帝国软件
  设为首页 加入收藏 关于我们
 
解密帝国网站管理系统
栏 目:
 
您的位置:首页 > 技术文档 > JAVA编程 >
在EJB环境中实现“观察者”模式
作者:未知 发布时间:2005-03-12 来源:JSP天空网
在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,则返回为指定发布者类和它的所有父类
* 注册的监听器提供者;如为flase,则只返回为指定发布者类注册的监听
* 器提供者
* @return 相应的监听器提供者的集合(永不为null)
*/
private synchronized Collection getListenerSuppliersCopy(Class
publisherClass, boolean checkInheritance){
if ( checkInheritance ) {
Collection publishers = myListenerSuppliersMap.keySet();
Iterator i = publishers.iterator();
Collection listenerSuppliers = new ArrayList();
while (i.hasNext()) {
Class nextPublisherClass = (Class)i.next();
if ( nextPublisherClass.isAssignableFrom(publisherClass) ) {
if ( myListenerSuppliersMap.get(nextPublisherClass) != null ) {
listenerSuppliers.addAll((Collection)
myListenerSuppliersMap.get(nextPublisherClass));
}
}
}
return listenerSuppliers;
} else {
Collection listenerSuppliers = (Collection)
myListenerSuppliersMap.get(publisherClass);
if ( listenerSuppliers == null ) {
return Collections.EMPTY_LIST;
} else {
//如果你决定不使用ArrayList,请改用clone支持的其他cast方式
return (ArrayList)((ArrayList)listenerSuppliers).clone();
}
}
}

/**
* 一个确保值非null的简单方法
*/
protected void assertNotNull(String message, Object object){
if ( object == null ) {
throw new IllegalArgumentException(message);
}
}
//保存“发布者类 -> 监听器提供者的集合”对
protected HashMap myListenerSuppliersMap = new HashMap();
private static DefaultListenerRegistry instance = new DefaultListenerRegistry();
}


第二个提供者示例允许使用无状态会话Bean作为监听器。它用会话Bean的一个EJBHome构造,利用create()方法返回一个可用的无状态会话Bean。请注意这个提供者在WebLogic 5.1下运行,这是因为,该服务器串行化对无状态Bean的调用,不会因为同时使用同一无状态Bean而抛出RemoteException异常。

//StatelessSupplier.javapackage com.jwasp.listener;
import java.util.EventListener;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
import java.rmi.RemoteException;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
/**
* 在getListener方法中,利用指定的监听器(无状态会话Bean)
* 的EJBHome调用create()方法返回一个监听器。请确保EJB容
* 器串行化了对监听器的调用(或者在fireXXX方法中提供对监
* 听器的同步)。
*/
public class StatelessSupplier implements ListenerSupplier {
public StatelessSupplier(EJBHome ejbHome) throws RemoteException{
if ( ejbHome == null ) {
throw new IllegalArgumentException("EJBHome is null");
} else if ( !ejbHome.getEJBMetaData().isStatelessSession() ) {
throw new IllegalArgumentException
("EJBHome should belong to stateless session bean");
}
myEjbHome = ejbHome;
}
public EventListener getListener(Class publisherClass)
throws RemoteException, ListenerActivationException {
try {
Method createMethod = myEjbHome.getClass().getMethod("create", new Class[0]);
Object object = createMethod.invoke(myEjbHome, new Object[0]);
if ( object instanceof EventListener ) {
return (EventListener)object;
} else {
throw new ListenerActivationException("Remote interface doesn´t extend EventListener");
}
} catch(NoSuchMethodException nsme) {
throw new ListenerActivationException("Home interface doesn´t define create() method");
} catch(SecurityException se) {
throw new ListenerActivationException(se.getMessage());
} catch(IllegalAccessException iae) {
throw new ListenerActivationException(iae.getMessage());
} catch(IllegalArgumentException iare) {
throw new ListenerActivationException(iare.getMessage());
} catch(InvocationTargetException ite) {
throw new ListenerActivationException(ite.getTargetException().getMessage());
}
}
private EJBHome myEjbHome;
}


■ 结束语
本文描述的框架允许你使用分布式的观察者/监听器(不错,监听器也可以是EJB;提供者将负责创建/定位EJB)。但是,它的目标不是替代JMS或消息驱动的Bean,该框架的目标是补充那两种强大的通知机制。结合JMS、消息驱动的Bean,本文描述的框架允许你在面对分布式事件发布者和监听器时,正确地实现Observer模式。

注意发布者不必一定是EJB。当发布者是普通的类时,你同样可以方便地应用本文所描述的框架。

最后,我想要指出,该框架的本质是:在发布者和观察者当时不存在的情况下,建立两者之间的关系。因此,任何想要发布者在创建时配置自己的地方,都可以使用本文所介绍的框架(如果发布者是一个EJB,则配置应该在ejbCreate()/ejbActivate()方法里进行;如果发布者是一个普通的类,则应该在构造函数里进行)。按照这种方法,如果发布者不是一个singleton,你不必担心所有的实例都有一组同样的监听器(或一组同样的实现业务逻辑的功能)。

下载本文的源代码:

http://www.ccidnet.com/tech/focus/java_little_t/ImplementObserverPattern.zip
  
评论】【加入收藏夹】【 】【打印】【关闭
※ 相关链接
无相关信息

   栏目导行
  PHP编程
  ASP编程
  ASP.NET编程
  JAVA编程
   站点最新
·致合作伙伴的欢迎信
·媒体报道
·帝国软件合作伙伴计划协议
·放眼未来 帝国近期将有重大举措!
·PHPWind6.3.2版通行证发布
·帝国备份王2008版正式发布
·帝国备份王2008版发布
·phpcms2007转帝国CMS5.0程序发布
·dedecms5.1转帝国CMS5.0程序发布
·帝国网站管理系统V5.0商业购买说明
   类别最新
·谈谈JDBC
·JDBC专题介绍
·JDBC接口技术
·利用weblogic的POOL(连接池)连接
·Jsp中调用Oracle存储过程的小例子
·JSP数据库操作例程
·JSP数据库连接大全
·用连接池提高Servlet访问数据库的效
·一种简单JDBC连接池的实现
·数据库连接池Java实现小结
 
关于帝国 | 广告服务 | 联系我们 | 程序开发 | 网站地图 | 留言板 帝国网站管理系统