Spring中观察者模式的应用
上一篇我们学了解读了Spring框架源码中的工厂模式,相信大家肯定都收获颇多,今天我们再来看看Spring源码中大佬是怎么使用观察者模式的。
观察者模式与发布订阅模式的异同
观察者模式它是用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应的作出反应。
在观察者模式中发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以应对多个观察者,而且这些观察者之间可以没有任何相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。
观察者模式的别名有发布-订阅(Publish/Subscribe)模式,我们来看一下观察者模式与发布订阅模式结构上有何区别。
- 在设计模式结构上,发布订阅模式继承自观察者模式,是观察者模式的一种实现的变体。
- 在设计模式意图上,两者关注点不同,一个关心数据源,一个关心的是事件消息。

观察者模式里,只有两个角色 —— 观察者和被观察者;而发布订阅模式里,却不仅仅只有发布者和订阅者两个角色,还有一个管理并执行消息队列的 “经纪人Broker”
观察者和被观察者,是松耦合的关系;发布者和订阅者,则完全不存在耦合
-
观察者模式:数据源直接通知订阅者发生改变。
-
发布订阅模式:数据源告诉第三方(事件通道)发生了改变,第三方再通知订阅者发生了改变。
Spring中的观察者模式
Spring 基于观察者模式,实现了自身的事件机制也就是事件驱动模型,事件驱动模型通常也被理解成观察者或者发布/订阅模型。
spring事件模型提供如下几个角色
- ApplicationEvent
- ApplicationListener
- ApplicationEventPublisher
- ApplicationEventMulticaster
接下来我们一一详细介绍。
1) 事件:ApplicationEvent
-
它是所有事件对象的父类。ApplicationEvent 继承自 jdk 的 EventObject,所有的事件都需要继承 ApplicationEvent,并且通过 source 得到事件源。
public abstract class ApplicationEvent extends EventObject { private static final long serialVersionUID = 7099057708183571937L; private final long timestamp = System.currentTimeMillis(); public ApplicationEvent(Object source) { super(source); } public final long getTimestamp() { return this.timestamp; } } -
Spring 也为我们提供了很多内置事件:
-
ContextRefreshEvent,当ApplicationContext容器初始化完成或者被刷新的时候,就会发布该事件;
-
ContextStartedEvent,当ApplicationContext启动的时候发布事件;
-
ContextStoppedEvent,当ApplicationContext容器停止的时候发布事件;
-
RequestHandledEvent,只能用于DispatcherServlet的web应用,Spring处理用户请求结束后,系统会触发该事件。
-

2) 事件监听:ApplicationListener
-
ApplicationListener(应用程序事件监听器) 继承自 jdk 的 EventListener ,所有的监听器都要实现这个接口,这个接口只有一个
onApplicationEvent()方法,该方法接受一个 ApplicationEvent 或其子类对象作为参数 -
在方法体中,可以通过不同对Event类的判断来进行相应的处理。当事件触发时所有的监听器都会收到消息,如果你需要对监听器的接收顺序有要求,可是实现该接口的一个实现SmartApplicationListener,,通过这个接口可以指定监听器接收事件的顺序。
@FunctionalInterface public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { void onApplicationEvent(E var1); } -
实现了 ApplicationListener 接口之后,需要实现方法
onApplicationEvent(),在容器将所有的 Bean都初始化完成之后,就会执行该方法。
3) 事件源:ApplicationEventPublisher
-
事件的发布者,封装了事件发布功能方法接口,是 Applicationcontext 接口的超类
事件机制的实现需要三个部分:事件源、事件和事件监听器。在上面介绍的 ApplicationEvent 就相当于事件,ApplicationListener 相当于事件监听器,这里的事件源说的就是ApplicationEventPublisher。
public interface ApplicationEventPublisher { default void publishEvent(ApplicationEvent event) { this.publishEvent((Object)event); } //调用publishEvent方法,传入一个ApplicationEvent的实现类对象作为参数,每当ApplicationContext发布ApplicationEvent时,所有的ApplicationListener就会被自动的触发. void publishEvent(Object var1); } -
我们常用的 ApplicationContext 都继承了AbstractApplicationContext,像我们平时常见ClassPathXmlApplicationContext、XmlWebApplicationContex也都是继承于它,AbstractApplicationContext 是 ApplicationContext 接口的抽象实现类,在该类中实现了publishEvent 方法:
protected void publishEvent(Object event, @Nullable ResolvableType eventType) { Assert.notNull(event, "Event must not be null"); if (this.earlyApplicationEvents != null) { this.earlyApplicationEvents.add(applicationEvent); } else { //事件发布委托给applicationEventMulticaster this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType); } }在这个方法中,我们看到了一个
getApplicationEventMulticaster()。这就要牵扯到另一个类ApplicationEventMulticaster。
4) 事件管理:ApplicationEventMulticaster
-
用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的,它的作用是把 Applicationcontext 发布的 Event 广播给它的监听器列表。
public interface ApplicationEventMulticaster { // 添加事件监听器 void addApplicationListener(ApplicationListener<?> var1); // 添加事件监听器,使用容器中的bean void addApplicationListenerBean(String var1); // 移除事件监听器 void removeApplicationListener(ApplicationListener<?> var1); void removeApplicationListenerBean(String var1); // 移除所有 void removeAllListeners(); // 发布事件 void multicastEvent(ApplicationEvent var1); void multicastEvent(ApplicationEvent var1, @Nullable ResolvableType var2); } -
在 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); } } }
自定义事件监听案例
实现一个需求:当调用一个类的方法完成时,该类发布事件,事件监听器监听该类的事件并执行的自己的方法逻辑
假设这个类是Request、发布的事件是ReuqestEvent、事件监听者是ReuqestListener。当调用Request的doRequest方法时,发布事件。
代码如下
/**
* 定义事件
**/
public class RequestEvent extends ApplicationEvent {
public RequestEvent(Object source) {
super(source);
}
}
/**
* 发布事件
**/
@Component
public class Request {
@Autowired
private ApplicationContext applicationContext;
public void doRequest(){
System.out.println("调用Request类的doRequest方法发送一个请求......");
applicationContext.publishEvent(new RequestEvent(this));
}
}
/**
* 监听事件
**/
@Component
public class RequestListener implements ApplicationListener<RequestEvent> {
@Override
public void onApplicationEvent(RequestEvent requestEvent) {
System.out.println("监听到RequestEvent事件,执行本方法");
}
}
public class SpringEventTest {
public static void main(String[] args) {
ApplicationContext context =
new AnnotationConfigApplicationContext("com.mashibing.pubsub");
Request request = (Request) context.getBean("request");
//调用方法发布事件
request.doRequest();
}
}
//打印日志
调用Request类的doRequest方法发送一个请求......
监听到RequestEvent事件,执行本方法
事件机制工作流程
上面代码的执行流程是什么呢?下面我们来看下图。

-
监听器什么时候注册到IOC容器
注册的开始逻辑是在 AbstractApplicationContext 类的
refresh()方法,该方法包含了整个 IOC 容器初始化所有方法。其中有一个registerListeners()方法就是注册系统监听者( Spring 自带的)和自定义监听器的。public void refresh() throws BeansException, IllegalStateException { //BeanFactory准备工作完成后进行的后置处理工作 this.postProcessBeanFactory(beanFactory); //执行BeanFactoryPostProcessor的方法; this.invokeBeanFactoryPostProcessors(beanFactory); //注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执行 this.registerBeanPostProcessors(beanFactory); //初始化MessageSource组件(做国际化功能;消息绑定,消息解析); this.initMessageSource(); //初始化事件派发器 this.initApplicationEventMulticaster(); ////子类重写这个方法,在容器刷新的时候可以自定义逻辑;如创建Tomcat,Jetty等WEB服务器 this.onRefresh(); //注册应用的监听器。就是注册实现了ApplicationListener接口的监听器bean,这些监听器是注册到ApplicationEventMulticaster中的 this.registerListeners(); //初始化所有剩下的非懒加载的单例bean this.finishBeanFactoryInitialization(beanFactory); //完成context的刷新 this.finishRefresh(); }看 registerListeners 的关键方法体,其中的两个方法
addApplicationListener和addApplicationListenerBean,从方法可以看出是添加监听者。protected void registerListeners() { Iterator var1 = this.getApplicationListeners().iterator(); while(var1.hasNext()) { ApplicationListener<?> listener = (ApplicationListener)var1.next(); this.getApplicationEventMulticaster().addApplicationListener(listener); } String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false); String[] var7 = listenerBeanNames; int var3 = listenerBeanNames.length; for(int var4 = 0; var4 < var3; ++var4) { String listenerBeanName = var7[var4]; this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName); } }那么最后将监听者放到哪里了呢?就是ApplicationEventMulticaster接口的子类

该接口主要两个职责,维护ApplicationListener相关类和发布事件。
实现在默认实现类 AbstractApplicationEventMulticaster,最后将Listener放到了内部类ListenerRetriever 两个set集合中`
private class ListenerRetriever {
public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet();
public final Set<String> applicationListenerBeans = new LinkedHashSet();
}
ListenerRetriever被称为监听器注册表。
-
Spring如何发布的事件并通知监听者
这个注意的有两个方法
1) publishEvent方法
AbstractApplicationContext实现了ApplicationEventPublisher接口的publishEvent方法
protected void publishEvent(Object event, @Nullable ResolvableType eventType) { Assert.notNull(event, "Event must not be null"); Object applicationEvent; //尝试转换为ApplicationEvent或者PayloadApplicationEvent,如果是PayloadApplicationEvent则获取eventType if (event instanceof ApplicationEvent) { applicationEvent = (ApplicationEvent)event; } else { applicationEvent = new PayloadApplicationEvent(this, event); if (eventType == null) { eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType(); } } if (this.earlyApplicationEvents != null) { //判断earlyApplicationEvents是否为空(也就是早期事件还没有被发布-说明广播器还没有实例化好),如果不为空则将当前事件放入集合 this.earlyApplicationEvents.add(applicationEvent); } else { //否则获取ApplicationEventMulticaster调用其multicastEvent将事件广播出去。本文这里获取到的广播器实例是SimpleApplicationEventMulticaster。 this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType); } //将事件交给父类处理 if (this.parent != null) { if (this.parent instanceof AbstractApplicationContext) { ((AbstractApplicationContext)this.parent).publishEvent(event, eventType); } else { this.parent.publishEvent(event); } } }2) multicastEvent方法
继续进入到
multicastEvent()方法,该方法有两种方式调用invokeListener,通过线程池和直接调用,进一步说就是通过异步和同步两种方式调用。public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) { //解析事件类型 ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event); //获取执行器 Executor executor = this.getTaskExecutor(); // 获取合适的ApplicationListener,循环调用监听器的onApplicationEvent方法 Iterator var5 = this.getApplicationListeners(event, type).iterator(); while(var5.hasNext()) { ApplicationListener<?> listener = (ApplicationListener)var5.next(); if (executor != null) { //如果executor不为null,则交给executor去调用监听器 executor.execute(() -> { this.invokeListener(listener, event); }); } else { //否则,使用当前主线程直接调用监听器; this.invokeListener(listener, event); } } }3) invokeListener方法
// 该方法增加了错误处理逻辑,然后调用doInvokeListener protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) { ErrorHandler errorHandler = this.getErrorHandler(); if (errorHandler != null) { try { this.doInvokeListener(listener, event); } catch (Throwable var5) { errorHandler.handleError(var5); } } else { this.doInvokeListener(listener, event); } } private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { //直接调用了listener接口的onApplicationEvent方法 listener.onApplicationEvent(event); }
有没有发现Spring框架中大佬这设计模式用得竟然如何丝滑?说实话,我看了感觉收获颇多,他们就是我们的目标!冲!
感谢您的阅读,转发、收藏和关注!愿我们每天进步一点点,让实力点点增强!你还知道该模式有其他的适用场景嘛?欢迎评论区留言!家人们也可以点击下方卡片,扫描二维码关注我公众号,交一个喜欢技术的朋友,了解更多技术分享!JAVA之路,愿我们并肩同行,一起成长!
往期精彩回顾
行为型设计模式:在生活中处处可见观察者模式的应用场景,你不能不知道
结构型设计模式:读了这篇外观模式,突然发现它远比想象中的更常用
结构型设计模式:一定要学好享元模式,可帮你省不少内存,这可都是Money啊

被折叠的 条评论
为什么被折叠?



