前言
在 Spring 运用中,观察者模式运用的场景很多,只不过在 Spring 内部为观察者模式定义为了抽象,使用多路广播器方式将观察者、被观察者、行为抽象了事件、监听器、事件源等名,下面对该内容进行详细分析
观察者模式/监听机制
监听器:模式中的观察者角色
多路广播器:被观察者触发事件调用,多个观察者监听到事件处理对应方法的逻辑
在 Spring 里面其实就是运用观察者模式进行设计的,它对比传统的观察者模式基础上进行了加工,概念上已经改为了基于事件驱动触发行为动作,下面对传统观察者模式、Spring 加工后的观察者模式进行介绍
观察者模式(传统)
观察者模式又称为发布/订阅(Publish/Subscribe)模式,在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新触发行为
观察者、被观察者共有行为
/** * 被观察者 */ public interface Observable { public void addObserver(Observer observer); public void deleteObserver(Observer observer); public void notifyObserver(String str); } /** * 观察者 */ public interface Observer { public void make(String str); }
被观察者实现类
public class BadMan implements Observable { private ArrayList<Observer> observers = new ArrayList<Observer>(); @Override public void addObserver(Observer observer) { this.observers.add(observer); } @Override public void deleteObserver(Observer observer) { this.observers.remove(observer); } @Override public void notifyObserver(String str) { for (Observer observer1 : observers) { observer1.make(str); } } public void run(){ System.out.println("罪犯要逃跑了"); this.notifyObserver("追击罪犯"); } public void play(){ System.out.println("罪犯在玩"); this.notifyObserver("不动做任何事情,静观其变"); } }
观察者
public class Police implements Observer { @Override public void make(String str) { System.out.println("police(警察)开始行动"); System.out.println("--------"+str); } } public class Soldier implements Observer { @Override public void make(String str) { System.out.println("军官(soldier)开始行动"); System.out.println("====="+str); } }
测试类
public class Test { public static void main(String[] args) { // 创建被观察者 BadMan bm = new BadMan(); // 创建观察者 Police gm = new Police(); Soldier gm2 = new Soldier(); // 向被观察者中添加观察者 bm.addObserver(gm); bm.addObserver(gm2); // 等待罪犯触发某些行为 bm.run(); } }
java.util 包下有提供 Observer
接口、Observable
类
- 被观察者:存储一个观察者的集合,执行不同动作时,要调用观察者的方法进行处理
- 观察者:看到被观察者不同行为的时候触发的反应
观察者模式(Spring)
在 Spring 中,观察者模式被理解为 “事件驱动”,通过对传统模式的理解进行细化拆分
基本概念
- 事件:被观察者具体要执行的动作「代码中的 run、play 方法」被拆分成事件,所有事件都实现于
ApplicationEvent
接口,交给子类在不同的事件情况时去执行自己内部的逻辑 - 监听器:作为观察者,可能存在多个,接受不同的事件去做不同的处理工作,所有监听器都实现于
ApplicationListener
接口,重写 onApplicationEvent 方法,方法内部通过不同的事件【ApplicationEvent】类型进行调用 - 多播器:把被观察者遍历观察者列表通知的消息操作拿出来,委托给一个多播器来进行消息通知,或者说通过观察者进行不同的操作,一般是
ApplicationEventMulticaster 子类 SimpleApplicationEventMulticaster
进行事件广播操作 - 事件源:谁来调用或者执行发布具体的事件,如在 AbstractApplicationContext 中调用
publishEvent(new ContextRefreshedEvent(this))
发布事件,比如 AbstractApplicationContext 就是指的事件源,它实现了 ApplicationEventPublisher 接口,可以调用 publishEvent 方法,类图如下
事件驱动准备工作及执行过程
事件驱动的准备工作如下:
- 提前准备好 N 多个事件
- 初始化多播器(创建多播器对象,此多播器对象中应该包含一个监听器的集合)
- 准备好一系列的监听器,向多播器中注册进去需要执行的监听器
- 准备事件发布,通知多播器循环调用监听器
onApplicationEvent
方法进行相关的逻辑处理工作
Spring 相关的源码
初始化事件监听多路广播器
protected void initApplicationEventMulticaster() { // 获取当前bean工厂,一般是DefaultListableBeanFactory ConfigurableListableBeanFactory beanFactory = getBeanFactory(); // 判断容器中是否存在bdName为applicationEventMulticaster的bd,也就是说自定义的事件监听多路广播器,必须实现ApplicationEventMulticaster接口 if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) { // 如果有,则从bean工厂得到这个bean对象 this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class); if (logger.isTraceEnabled()) { logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]"); } } else { // 如果没有,则默认采用 SimpleApplicationEventMulticaster this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory); beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster); if (logger.isTraceEnabled()) { logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " + "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]"); } } }
默认采用的是 SimpleApplicationEventMulticaster 多路广播器,它的父类 AbstractApplicationEventMulticaster
内部类专门使用了一个集合来存储所有加载的监听器
// 创建监听器助手类,用于存放应用程序的监听器集合,参数是否是预过滤监听器为false private final ListenerRetriever defaultRetriever = new ListenerRetriever(false); private class ListenerRetriever { // ApplicationListener 对象集合 public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>(); // BeanFactory 中的 applicationListener 类型 Bean 名集合 // 在设置监听器时会通过 beanName 去获取监听器,再放入 applicationListeners 集合中 public final Set<String> applicationListenerBeans = new LinkedHashSet<>(); ....... }
注册监听器的源码:在所有注册的 bean 中查找监听器,将监听器注册到消息广播器中
protected void registerListeners() { // 遍历应用程序中存在的监听器集合,并将对应的监听器添加到监听器的多路广播器中 for (ApplicationListener<?> listener : getApplicationListeners()) { getApplicationEventMulticaster().addApplicationListener(listener); } // 从容器中获取所有实现了ApplicationListener接口的bd的bdName // 放入 ApplicationListenerBeans 集合中 String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); for (String listenerBeanName : listenerBeanNames) { getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName); // 可以直接获取监听器实例进去,无须在取用的时候再次 getBean getApplicationEventMulticaster().addApplicationListener(this.getBean(listenerBeanName,ApplicationListener.class)); } // 此处先发布早期的监听器集合 Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents; this.earlyApplicationEvents = null; if (!CollectionUtils.isEmpty(earlyEventsToProcess)) { for (ApplicationEvent earlyEvent : earlyEventsToProcess) { getApplicationEventMulticaster().multicastEvent(earlyEvent); } } }
事件驱动的逻辑执行过程如下:
- 事件源来发布不同的事件
- 当发布事件之后会调用多播器的方法来进行事件广播操作,由多播器去触发具体的监听器去执行操作
- 监听器接收到具体的事件之后,可以验证是否能处理当前事件的类型,如果可以,进行处理;如果不行,不做任何操作