Spring事件驱动
提示: 本材料只做个人学习参考,不作为系统的学习流程,请注意识别!!!
一. 事件驱动简介
- 事件驱动模型基于发布-订阅的编程模型,设计思想为:观察者设计模式。
- 定义了对象之间的一对多关系,当一个对象状态发生改变(发布),其观察者(订阅者)就可以接收到其改变的通知。
- 观察者是如何处理的,发布者是无需干涉,这也是事件驱动主要用来进行代码解耦原因。
事件驱动模型的核心构建包含:
- 事件源:负责生产事件的对象。比如前端button按钮。
- 事件监听器(处理器):负责处理事件的对象。
- 事件(事件对象):事件源和事件监听之间的信息桥梁,是整个事件驱动模型的核心。
二. 观察者(Observer)模式简介
观察者模式:
指多个对象之间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都能够得到通知并被自动更新。又称:发布-订阅模式、模型-视图模式,它是对象行为型模式。
优点:
观察者和被观察者是抽象耦合的,他们之间形成了一套触发机制。
缺点:
如果被观察者有很多直接或间接观察者的话,通知所有的观察者可能会耗费很长的时间。
如果观察者和被观察者之间形成循环依赖的话,观察目标会触发它们之间的循环调用,引起系统崩溃。
观察者模式不能够让观察者知道所观察目标对象是如何发生变化的,而仅仅知道观察目标发生了变化。
示例代码:
发布者:
import java.util.ArrayList;
import java.util.List;
/**
* 发布者
*/
public class Subject {
//观察者列表
private List<Observer> observers = new ArrayList<Observer>();
//状态
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyAllObservers();
}
public void attach(Observer observer) {
observers.add(observer);
}
/**
* 通知所有的观察者
*/
public void notifyAllObservers() {
for (Observer observer : observers) {
observer.update();
}
}
}
观察者抽象类:
/**
* 观察者抽象类
*/
public abstract class Observer {
//观察者观察的发布者
protected Subject subject;
//观察者事件处理方法
public abstract void update();
}
观察者实现类:
public class Observer1 extends Observer {
public Observer1(Subject subject) {
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println("Observer1111..." + subject.getState());
}
}
public class Observer2 extends Observer {
public Observer2(Subject subject) {
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println("Observer2222..." + subject.getState());
}
}
测试类:
public class ObserverPatternDemo {
public static void main(String[] args) {
Subject subject = new Subject();
new Observer1(subject);
new Observer2(subject);
System.out.println("First state change: 111111");
subject.setState(111111);
System.out.println("Second state change: 222222");
subject.setState(222222);
}
}
测试结果:
First state change: 111111
Observer1111...111111
Observer2222...111111
Second state change: 222222
Observer1111...222222
Observer2222...222222
除了上述自定义的观察者模式外,javaBean规范中提供了一种监听属性变化的事件驱动模型(这里我们不做拓展)。此外JDK为我们提供了现成的观察者实现,简单介绍(附部分源码):
观察者接口:
public interface Observer {
/**
* This method is called whenever the observed object is changed. An
* application calls an <tt>Observable</tt> object's
* <code>notifyObservers</code> method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the <code>notifyObservers</code>
* method.
*/
void update(Observable o, Object arg);
}
被观察者接口:
package java.util;
/**
* @author Chris Warth
* @see java.util.Observable#notifyObservers()
* @see java.util.Observable#notifyObservers(java.lang.Object)
* @see java.util.Observer
* @see java.util.Observer#update(java.util.Observable, java.lang.Object)
* @since JDK1.0
*/
public class Observable {
private boolean changed = false;
private Vector<Observer> obs;
/** Construct an Observable with zero Observers. */
public Observable() {
obs = new Vector<>();
}
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
public void notifyObservers() {
notifyObservers(null);
}
public void notifyObservers(Object arg) {
Object[] arrLocal;
synchronized (this) {
/* 这里存在2个潜在的糟糕结果:
* 1) 一个新加入的观察者错过了正在执行的通知
* 2) 一个刚刚删除的观察者不需要关心变更时有可能会错误地被通知
*/
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
public synchronized void deleteObservers() {
obs.removeAllElements();
}
protected synchronized void setChanged() {
changed = true;
}
protected synchronized void clearChanged() {
changed = false;
}
public synchronized boolean hasChanged() {
return changed;
}
public synchronized int countObservers() {
return obs.size();
}
}
三.Spring事件驱动
1. 原理解析
Spring的事件驱动模型由三部分组成:
- 事件:ApplicationEvent,继承自JDK的EventObject,所有事件将继承它,并通过source得到事件源。
- 事件订阅者:ApplicationListener,继承自JDK的EventListener,所有监听器将继承它。
- 事件发布者:ApplicationEventPublisher及ApplicationEventMulticaster接口,使用这个接口,我们的Service就拥有了发布事件的能力。
下面对这几个类做下介绍:
ApplicationEvent:
public abstract class ApplicationEvent extends EventObject {
private static final long serialVersionUID = 7099057708183571937L;
private final long timestamp;
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
public final long getTimestamp() {
return this.timestamp;
}
}
ApplicationEvent,继承自JDK的EventObject,所有事件将继承它,并通过source得到事件源。该类的实现类ApplicationContextEvent表示ApplicaitonContext的容器事件
public abstract class ApplicationContextEvent extends ApplicationEvent {
public ApplicationContextEvent(ApplicationContext source) {
super(source);
}
public final ApplicationContext getApplicationContext() {
return (ApplicationContext) getSource();
}
}
ApplicationListener:
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}
ApplicationListener继承自jdk的EventListener,所有的监听器都要实现这个接口,这个接口只有一个onApplicationEvent()方法,该方法接受一个ApplicationEvent或其子类对象作为参数。在方法体中,可以通过不同对Event类判断来进行相应的处理。当事件触发时所有的监听器都会收到消息,如果你需要对监听器的接收顺序有要求,可是实现该接口的一个实现SmartApplicationListener,通过这个接口可以指定监听器接收事件的顺序。
ApplicationContext:
ApplicationContext是spring中的全局容器,即"应用上下文"(IOC容器),它用来负责读取bean的配置文档,管理bean的加载,维护bean之间的依赖关系,可以说是负责bean整个生命周期。
Application作为一个事件源,需要显示的调用publishEvent方法,传入一个ApplicationEvent实现类对象,每当ApplicationContext发布ApplicationEvent时,所有的ApplicationListener就会被自动的触发。
ApplicationContext接口实现了ApplicationEventPublisher接口,后者有一个很重要的方法:
@FunctionalInterface
public interface ApplicationEventPublisher {
//......
void publishEvent(Object event);
}
我们常用的ApplicationContext都继承了AbstractApplicationContext,例如:ClassPathXmlApplicationContext、XmlWebApplicationContex,AbstractApplicationcontext是ApplicationContext接口的抽象实现类,在该类中实现了publishEvent方法:
@Override
public void publishEvent(ApplicationEvent event) {
publishEvent(event, null);
}
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
}
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
}
}
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
这个方法中有个getApplicationEventMulticaster()方法,这就要牵扯到另一个类ApplicationEventMulticaster:
ApplicationEventMulticaster:
它属于事件广播器,作用是把Applicationcontext发布的Event广播给所有的监听器。在AbstractApplicationcontext中有一个applicationEventMulticaster成员变量,提供了监听器Listener的注册方法:
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext, DisposableBean {
private ApplicationEventMulticaster applicationEventMulticaster;
protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String lisName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(lisName);
}
}
}
2. Spring 事件监听Demo
Demo1:
事件类:
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
System.out.println("MyEvent...");
}
public void print(){
System.out.println("MyEvent print()...");
}
}
监听类:
public class MyListener1 implements ApplicationListener{
public void onApplicationEvent(ApplicationEvent event) {
if(event instanceof MyEvent){
System.out.println("MyListener1 ...");
MyEvent myEvent=(MyEvent)event;
myEvent.print();
}
}
}
public class MyListener2 implements ApplicationListener{
public void onApplicationEvent(ApplicationEvent event) {
if(event instanceof MyEvent){
System.out.println("MyListener2 ...");
MyEvent myEvent=(MyEvent)event;
myEvent.print();
}
}
}
发布事件的类:
public class MyPubisher implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
public void publishEvent(ApplicationEvent event){
System.out.println("MyPubisher publishEvent() ...");
applicationContext.publishEvent(event);
}
}
测试类:
public class MyTest {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("classpath:spring/application.xml");
MyPubisher myPubisher=(MyPubisher) context.getBean("myPublisher");
myPubisher.publishEvent(new MyEvent("666"));
}
}
Demo2:
事件类
/**
* 账号信息变更事件
*/
public class ModifyEvent extends ApplicationEvent {
private List<Integer> accNoList;
public ModifyEvent(final Object source,List<Integer> accNoList) {
super(source);
this.accNoList = accNoList;
}
public List<Integer> getAccNoList() {
return accNoList;
}
public void setAccNoList(List<Integer> accNoList) {
this.accNoList = accNoList;
}
}
监听类
@EnableAsync
@Component
public class ChangeListener {
private static final Logger log = LoggerFactory.getLogger(ChangeListener.class);
@EventListener //表示事件监听注解
@Async //可以异步处理
public void listenerModifyEvent(BalanceModifyEvent event) {
try {
Set<Integer> accNos = new HashSet<Integer>(event.getAccNoList());
//...
//业务逻辑
} catch (Exception e) {
log.error("业务处理异常", e);
}
}
}
事件发布类 && 测试类,参考上一个Demo。