详解Spring事件驱动模型2
http://jinnianshilongnian.iteye.com/blog/1902886事件驱动模型简介
事件驱动模型也就是我们常说的观察者,或者发布-订阅模型;理解它的几个关键点:
?
?
接下来先看一个用户注册的例子:
用户注册成功后,需要做这么多事:
1、加积分
2、发确认邮件
3、如果是游戏帐户,可能赠送游戏大礼包
4、索引用户数据
…………
问题:
?
从如上例子可以看出,应该使用一个观察者来解耦这些Service之间的依赖关系,如图:
?
增加了一个Listener来解耦UserService和其他服务,即注册成功后,只需要通知相关的监听器,不需要关系它们如何处理。增删功能非常容易。
?
这就是一个典型的事件处理模型/观察者,解耦目标对象和它的依赖对象,目标只需要通知它的依赖对象,具体怎么处理,依赖对象自己决定。比如是异步还是同步,延迟还是非延迟等。
?
上边其实也使用了DIP(依赖倒置原则),依赖于抽象,而不是具体。
?
还是就是使用了IoC思想,即以前主动去创建它依赖的Service,现在只是被动等待别人注册进来。
?
?
其他的例子还有如GUI中的按钮和动作的关系,按钮和动作本身都是一种抽象,每个不同的按钮的动作可能不一样;如“文件-->新建”打开新建窗口;点击“关闭”按钮关闭窗口等等。
?
主要目的是:松散耦合对象间的一对多的依赖关系,如按钮和动作的关系;
?
如何实现呢?面向接口编程(即面向抽象编程),而非面向实现。即按钮和动作可以定义为接口,这样它俩的依赖是最小的(如在Java中,没有比接口更抽象的了)。
?
有朋友会问,我刚开始学的时候也是这样:抽象类不也行吗?记住一个原则:接口目的是抽象,抽象类目的是复用;所以如果接触过servlet/struts2/spring等框架,大家都应该知道:
Servlet<-----GenericServlet<-----HttpServlet<------我们自己的Action<------ActionSupport<------我们自己的DaoInterface<------××DaoSupport<-----我们自己的从上边大家应该能体会出接口、抽象类的主要目的了。现在想想其实很简单。
?
在Java中接口还一个非常重要的好处:接口是可以多实现的,类/抽象类只能单继承,所以使用接口可以非常容易扩展新功能(还可以实现所谓的mixin),类/抽象类办不到。
?
Java GUI事件驱动模型/观察者?扯远了,再来看看Java GUI世界里的事件驱动模型吧:
?
如果写过AWT/Swing程序,应该知道其所有组件都继承自java.awt.Component抽象类,其内部提供了 addXXXListener(XXXListener l) 注册监听器的方法,即Component与实际动作之间依赖于XXXListener抽象。
?
比如获取焦点事件,很多组件都可以有这个事件,是我们知道组件获取到焦点后需要一个处理,虽然每个组件如何处理是特定的(具体的),但我们可以抽象 一个FocusListener,让所有具体实现它然后提供具体动作,这样组件只需依赖于FocusListener抽象,而不是具体。
?
还有如java.awt.Button,提供了一个addActionListener(ActionListener l),用于注册点击后触发的ActionListener实现。
?
组件是一个抽象类,其好处主要是复用,比如复用这些监听器的触发及管理等。
?
JavaBean规范的事件驱动模型/观察者JavaBean规范提供了JavaBean的PropertyEditorSupport及PropertyChangeListener支持。
?
PropertyEditorSupport就是目标,而PropertyChangeListener就是监听器,大家可以google搜索下,具体网上有很多例子。
?
Java提供的事件驱动模型/观察者抽象JDK内部直接提供了观察者模式的抽象:
目标:java.util.Observable,提供了目标需要的关键抽象:addObserver/deleteObserver/notifyObservers()等,具体请参考javadoc。
观察者:java.util.Observer,提供了观察者需要的主要抽象:update(Observable?o,?Object?arg),此处还提供了一种推模型(目标主动把数据通过arg推到观察者)/拉模型(目标需要根据o自己去拉数据,arg为null)。
?
因为网上介绍的非常多了,请google搜索了解如何使用这个抽象及推/拉模型的优缺点。
?
接下来是我们的重点:spring提供的事件驱动模型。
?
Spring提供的事件驱动模型/观察者抽象首先看一下Spring提供的事件驱动模型体系图:?
事件具体代表者是:ApplicationEvent:
1、其继承自JDK的EventObject,JDK要求所有事件将继承它,并通过source得到事件源,比如我们的AWT事件体系也是继承自它;
2、系统默认提供了如下ApplicationEvent事件实现:
只有一个ApplicationContextEvent,表示ApplicationContext容器事件,且其又有如下实现:
注:org.springframework.context.support.AbstractApplicationContext抽象类实现 了LifeCycle的start和stop回调并发布ContextStartedEvent和ContextStoppedEvent事件;但是无任 何实现调用它,所以目前无任何作用。
?
目标(发布事件者)具体代表者是:ApplicationEventPublisher及ApplicationEventMulticaster,系统默认提供了如下实现:
1、 ApplicationContext接口继承了ApplicationEventPublisher,并在 AbstractApplicationContext实现了具体代码,实际执行是委托给ApplicationEventMulticaster(可以 认为是多播):
这里讲解一下Spring对异步事件机制的支持,实现方式有两种:
?1、全局异步即只要是触发事件都是以异步执行,具体配置(spring-config-register.xml)如下:
?
spring3提供了@Aync注解来完成异步调用。此时我们可以使用这个新特性来完成异步调用。不仅支持异步调用,还支持简单的任务调度,比如我的项目就去掉Quartz依赖,直接使用spring3这个新特性,具体可参考spring-config.xml。
?
2.1、开启异步调用支持
?
使用@Async注解即可,非常简单。?
?
这样不仅可以支持通过调用,也支持异步调用,非常的灵活,实际应用推荐大家使用这种方式。
?
?
通过如上,大体了解了Spring的事件机制,可以使用该机制非常简单的完成如注册流程,而且对于比较耗时的调用,可以直接使用Spring自身的异步支持来优化。