QQRead:http://www.qqread.com/java/2006/10/e241073.html
在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,则返回为指定发布者类和它的所有父类
*进入讨论组讨论。
在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,则返回为指定发布者类和它的所有父类
*进入讨论组讨论。
相关图文阅读
频道图文推荐
健 康 咨 询
时 尚 咨 询
相关专题
- 125条常见的java面试笔试题大汇总 (23470次浏览)
- 十四种Java开发工具点评 (16011次浏览)
- JAVA中配置环境变量设置方法大全 (14586次浏览)
- 论Java外观模式 (1091次浏览)
- 英文打字练习 (718次浏览)
- Java编程十大典型问题详解(1) (637次浏览)
- Java路径问题最终解决方案 (598次浏览)
- Ruby,Java的劲敌 (582次浏览)
- Java学习从入门到精通(附FAQ)2 (579次浏览)
- JAVA高手解析XML配置文件的读取操作 (568次浏览)



