观察者模式(Observer):定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
上面是官方给出的观察者模式的定义,从我个人角度来讲,我觉得就把它称之为发布-订阅模式更为贴切。虽然看起来这个设计模式没什么难度,但是想把它真正应用到日常的代码开发中,还是得下一番功夫。本文首先会使用一个简单的例子来引导大家,让大家明白什么是观察者模式,然后再给大家讲解一下spring框架是如何使用这个观察者模式的(由于本人对于java语言更为熟悉,所以接下来的讲解全部使用的语言都是java)。
我们可以自己实现观察者模式,也可以使用java语言本身内置的观察者模式,通过java.util包中的Observer接口(观察者)与Observable类(被观察者)实现。接下来将使用java内置的观察者模式实现一个天气预报发布和订阅的小例子。
接下来我们实现上图天气预告的发布订阅。
package observation;
import java.util.Observable;
/**
天气预告发布者 发布天气信息给订阅者
*/
public class WeatherPublisher extends Observable {
//天气信息
private String weatherInfo;
//订阅者使用该方法主动获取天气信息
public String getWeatherInfo() {
return weatherInfo;
}
public void setWeatherInfo(String weatherInfo) {
this.weatherInfo = weatherInfo;
//在通知订阅者前必须调用该方法指示状态的改变
//为什么要调用该方法,我们且看后面的讲解
this.setChanged();
//采用“拉”的方式,订阅者主动拉取天气的变化信息
this.notifyObservers();
//与上面的相面,采用“推”的方式,将天气信息推送给订阅者
// this.notifyObservers(weatherInfo);
}
public static void main(String[] args) {
//定义一个天气预告发布者
WeatherPublisher weatherPublisher = new WeatherPublisher();
//添加小王和小李为天气预告订阅者
weatherPublisher.addObserver(new XiaoLiSubscriber());
weatherPublisher.addObserver(new XiaoWangSubscriber());
//发布天气信息
weatherPublisher.setWeatherInfo("今天天气很晴朗,阳光明媚,适合出游");
weatherPublisher.setWeatherInfo("明天是阴天,记得出门带伞哦");
}
}
package observation;
import java.util.Observable;
import java.util.Observer;
/**
天气订阅者小王
*/
public class XiaoWangSubscriber implements Observer {
//第二个参数即是使用“推”方式推送过来的天气信息,这里使用“拉”方式主动获取
public void update(Observable o, Object arg) {
if (o instanceof WeatherPublisher) {
WeatherPublisher publisher = (WeatherPublisher) o;
System.out.println(“小王主动获取天气信息:” + publisher.getWeatherInfo());
}
}
}
天气订阅者小李:
package observation;
import java.util.Observable;
import java.util.Observer;
/**
天气订阅者小李
*/
public class XiaoLiSubscriber implements Observer {
public void update(Observable o, Object arg) {
if (o instanceof WeatherPublisher) {
WeatherPublisher publisher = (WeatherPublisher) o;
System.out.println(“小李主动获取天气信息:” + publisher.getWeatherInfo());
}
}
}
运行结果如下:
小王主动获取天气信息:今天天气很晴朗,阳光明媚,适合出游
小李主动获取天气信息:今天天气很晴朗,阳光明媚,适合出游
小王主动获取天气信息:明天是阴天,记得出门带伞哦
小李主动获取天气信息:明天是阴天,记得出门带伞哦
其实上面的例子是很简单的,打开Observable类的源码仔细阅读就能明白。这里我们贴一下Observable类的伪代码:
从这里能看出来,为什么我们在通知前必须要调用setChange()这个方法,只有change表只为true时才能通知观察者。
上面说到jdk内置的观察者模式的一些缺点,当它无法满足使用者的需求时,我们可以自己实现一套。实现的方式并不困难,我们仍然使用上面天气预告的例子。实现的方式其实和jdk内置的观察者模式差不多,但是根据业务的不同,会有细节上的差别。
public interface Subject {
//注册观察者
public void registerObserver(Observer observer);
//删除观察者
public void removeObserver(Observer observer);
//通知所有观察者
public void notifyObservers();
}
public class WeatherSubject implements Subject {
private List<Observer> observers; private String weatherInfo; public WeatherSubject() { this.observers = new ArrayList<Observer>(); } public void registerObserver(Observer observer) { observers.add(observer); } public void removeObserver(Observer observer) { observers.remove(observer); } public void notifyObservers() { for (Observer observer : observers) { observer.update(weatherInfo); } } public String getWeatherInfo() { return weatherInfo; } public void setWeatherInfo(String weatherInfo) { this.weatherInfo = weatherInfo; notifyObservers(); } public static void main(String[] args) { WeatherSubject weatherSubject = new WeatherSubject(); weatherSubject.registerObserver(new XiaoWangObserver()); weatherSubject.registerObserver(new XiaoLiObserver()); weatherSubject.setWeatherInfo("今天天气很晴朗,阳光明媚,适合出游"); weatherSubject.setWeatherInfo("明天是阴天,记得出门带伞哦"); }
}
public interface Observer {
public void update(String wetherInfo);
}
public class XiaoLiObserver implements Observer {
public void update(String wetherInfo) {
System.out.println("小李收到天气信息:" + wetherInfo);
}
}
public class XiaoWangObserver implements Observer {
public void update(String wetherInfo) {
System.out.println("小王收到天气信息:" + wetherInfo);
}
}
运行结果如下:
小王收到天气信息:今天天气很晴朗,阳光明媚,适合出游
小李收到天气信息:今天天气很晴朗,阳光明媚,适合出游
小王收到天气信息:明天是阴天,记得出门带伞哦
小李收到天气信息:明天是阴天,记得出门带伞哦
看完这些,相信大家一定对观察者模式有一定的了解。下面我们就来看一看sping框架中是怎么使用观察者模式的。
spring框架,学过java的同学一定对它不陌生,spring对于设计模式的应用十分值得我们学习,下面我们就来看一看它是如何使用观察者模式的。
spring中事件机制的底层原理就是观察者模式。java其实也有自己的事件机制,spring的事件机制也是由java的事件机制拓展而来。spring提供的事件机制是通过ApplicationEvent类和ApplicationListener接口实现的。我们仍然使用上面的天气信息发布的例子来讲解spring的观察者模式。
1、首先我们需要实现一个事件,发布事件,必须要有事件才行,这里定义是天气事件
/** * 天气事件,包含待发布的天气信息 */ public class WeatherEvent extends ApplicationContextEvent {
private String weatherInfo; public WeatherEvent(ApplicationContext source, String weatherInfo) { super(source); this.weatherInfo = weatherInfo; } public String getWeatherInfo() { return weatherInfo; }
}
2、接下实现监听器,即天气监听者,我们实现3个监听者,我们仔细看一下有什么不同。
监听者1:
public class WeatherListen1 implements ApplicationListener<WeatherEvent> {
@Override public void onApplicationEvent(WeatherEvent event) { System.out.println("天气监听者1:" + event.getWeatherInfo()); }
}
监听者2:
public class WeatherListen2 implements ApplicationListener<WeatherEvent> {
@Override public void onApplicationEvent(WeatherEvent event) { System.out.println("天气监听者2:" + event.getWeatherInfo()); }
}
监听者3:
public class OtherListen3 implements ApplicationListener<PayloadApplicationEvent> {
@Override public void onApplicationEvent(PayloadApplicationEvent event) { System.out.println("其它天气监听者3:" + event.getPayload().toString()); }
}
监听者1与监听者2监听ApplicationContextEvent事件,监听者3监听的是PayloadApplicatinEvent事件。这里我们看一下这两个事件的区别在哪。首先看一下AbstractApplicationContext这个抽象类的源码,当中有一个publishEvent方法,所有的事件都是在这里被发布的。我们所熟知的ClassPathXmlApplicationContext与FileSystemXmlApplicationContext类都是继承这个类,最终都是调用这个类的publishEvent方法发布事件的。
图中红色框框中可知,除了ApplicationEvent外的事件都被包装成PayloadApplicatinEvent事件。
PayloadApplicatinEvent类的构造函数有两个参数,第二个参数就是我们发布出去的事件。我们可以通过getPayload()获取发布的事件。
如果我们代码里有一个事件想发布,却又没有继承ApplicationEvent,为了不影响现有的功能,这时发布的事件就可以以PayloadApplicatinEvent的形式发布。
3、还有天气的发布者
/** * 这里也可以使用ApplicationEventPublisher,ApplicationContext接口继承了它, * 通过实现ApplicationContextAware获取应用上下文对象,并调用publishEvent方法发布事件 */ public class WeatherPublisher implements ApplicationContextAware {
private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public void publishEvent(String weatherInfo) { WeatherEvent weatherEvent = new WeatherEvent(this.applicationContext, weatherInfo); //发布ApplicationContextEvent事件 this.applicationContext.publishEvent(weatherEvent); //发布PayloadApplicationEvent事件 this.applicationContext.publishEvent("今天天阴,请注意保暖!"); } public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml"); WeatherPublisher weatherPublisher = (WeatherPublisher) applicationContext.getBean("weatherPublisher"); weatherPublisher.publishEvent("今天天气阳光明媚!"); }
}
运行结果:
天气监听者1:今天天气阳光明媚!
天气监听者2:今天天气阳光明媚!
其它天气监听者3:今天天阴,请注意保暖!
事件的发布使用的是ApplicationContext,这个例子中的应用上下文是ClassPathXmlApplicationContext,讲到这里大家可能不太明白为什么使用应用上下文来发布事件,这个就需要我们来看看spring容器的源码了。究竟spring是如何形成这么一套完整的事件机制的。
spring在AbstractApplicationContext这个抽象类中实现了事件体系的搭建。AbstractApplicationContext的refresh()方法完成了spring容器的启动。事件机制的实现也是在这个过程中完成的。
这里我们只看事件机制的实现过程,其他部分(beanFactory等)会在别的文章中详细介绍。关于事件机制的主要就是以上三个函数了。
1、首先,初始化应用上下文广播器,我们可以自定义事件广播器,只要实现ApplicationEventMulticaster这个接口即可。如果没有实现自己的广播器,spring便会使用默认的SimpleApplicationEventMulticaster作为事件广播器。
说了这么多,究竟什么是事件广播器呢?
事件广播器的作用是把Applicationcontext发布的Event广播给所有的在spring中注册的监听器。事件其实是通过事件广播器发布给监者的。
2、注册事件监听器
所有的事件监听器由spring管理,进入registerListeners()函数我们可以发现,监听器的管理者也是事件广播器。关于事件监听器我们不再详述。
3、完成容器初始化并广播ContextRefreshedEvent事件
finishRefresh()函数不再详细展开。
本文源码:码云地址
到这里关于观察者模式的介绍就结束了,第一次写博客,有很多不足的地方请见谅,不正确的地方还请及时指出,本文的源码会放在github上,大家可以下载观看,谢谢!