源码级别的广播与监听实现
近期疫情形势严峻,情形不容乐观,周末也不敢出去浪了,躲在家里“葛优躺”。闲来无事,又翻了遍Spring
的源码。不翻不知道,一翻吓一跳,之前翻过的源码已经吃进了肚子里,再见亦是陌生人。
个人建议:为了以后能快速的捡起某个知识点,最好的方法还是形成文档,下次有遗漏的时候,直接读文档,按之前的思路捋一遍,“干净又卫生”。
之前的文章中我们已经介绍过如何在项目中快速上手“事件通知机制”,相信大家已经掌握了。但是我们作为高级javaer
,要知其然,更要知其所以然。今天就带大家从源码的角度来分析一下广播与监听的底层实现原理。
源码导入教程也给你准备好了,不来试试吗?
版本号:spring-framework-5.0.x
源码解析
为了实现广播与监听的功能,Spring
为我们提供了两个重要的函数式接口:ApplicationEventPublisher
和ApplicationListener
。前者的publishEvent()
方法为我们提供了发送广播的能力;后者的onApplicationEvent()
方法为我们提供了监听并处理事件的能力。
接下来我们就来分析一下spring
是如何运用这两种能力的。
不知道大家对单例对象的初始化调用过程是否熟悉?主要调用方法流程如下:
发送广播
applyBeanPostProcessorsBeforeInitialization
方法会去遍历该工厂创建的所有的Bean
后置处理器,然后去依次执行后置处理器对应的postProcessBeforeInitialization
方法。
在该方法的实现类中我们看到了两个熟悉的类名
不知道大家还记得不,这俩类是在beanFactory
的准备工作过程中添加的两个bean
的后置处理器,所以这个地方会依次去执行这两个类中的实现方法。
由于蓝框中类的实现方法是默认实现按照原样返回的给定的bean
,所以此处不用过多分析,我们重点来看下红框中类的方法实现。
该方法中最重要的是invokeAwareInterfaces
方法,它的作用是检测对应的bean
是否实现了某个Aware
接口,如果实现了的话就去进行相关的调用。
我们发现在invokeAwareInterfaces
方法中出现了如上代码,这不就是和广播发送相关的吗?所以只要我们写一个类来实现ApplicationEventPublisherAware
接口,就可以在该bean
中注入一个ApplicationEventPublisher
对象,也就获得了发送广播的能力。
监听消息
applyBeanPostProcessorsAfterInitialization
方法也会去遍历该工厂创建的所有的Bean
后置处理器,然后去依次执行后置处理器对应的postProcessAfterInitialization
方法。
同样的,该方法的实现类中也有ApplicationContextAwareProcessor
和ApplicationListenerDetector
两个类,但是不同的是,前者的类的实现方法是默认实现按照原样返回的给定bean
,而后者做了相关的处理。
上述代码是将实现了ApplicationListener
接口的bean
添加到监听器列表中,最终是保存在AbstractApplicationEventMulticaster
的成员变量defaultRetriever
的集合applicationListeners
中。
猜想:当发送广播消息时,就直接找到集合中的这些监听器,然后调用每个监听器的
onApplicationEvent
方法完成事件的处理。
案例分析
在refresh()
的finishRefresh()
方法中,
发送一条事件类型为ContextRefreshedEvent
的广播消息,用来代表Spring
容器初始化结束。通过分析发现,该方法中最主要的就是如下代码:
refresh()
的initApplicationEventMulticaster()
将applicationEventMulticaster
初始化为SimpleApplicationEventMulticaster
在实现类SimpleApplicationEventMulticaster
的方法中,会找到已注册的ApplicationListener
列表,然后分别调用invokeListener
方法(将监听和事件作为参数传到方法并执行的过程就是发送广播的过程)。
底层调用的是listener.onApplicationEvent(event);
方法,也就是各个监听实现类单独处理广播消息的逻辑。
消息与监听绑定
看到这儿,你是不是已经发现了:消息类型和监听器的绑定发生在广播过程中。接下来就让我们去一探究竟
我们看一下multicastEvent()
方法中的getApplicationListeners(event, type)
方法。
在该方法中,用到了ConcurrentHashMap
类型的缓存retrieverCache
,所以每种类型的事件在广播的时候会触发一次绑定操作。它的 key 由事件的来源和类型确定,它的 value 中就包含了由事件来源和类型所确定的所有监听列表。
其中绑定的逻辑就出现在retrieveApplicationListeners
方法中,大家可以去源码中查看。
实战教学
纸上得来终觉浅,绝知此事要躬行。为了更好地理解广播与监听的流程,我们当然得用实战来加以辅佐!
自定义事件
自定义广播
MyPublisher
实现了ApplicationEventPublisherAware
接口 ,在spring
初始化(见上文中的invokeAwareInterfaces
)的时候会回调setApplicationEventPublisher
方法,获取到初始化(添加bean
后置处理器ApplicationContextAwareProcessor
)时的AbstractApplicationContext
,而AbstractApplicationContext
又间接实现了ApplicationEventPublisher
而获得发送能力。真正执行的是 AbstractApplicationContext
类中的 publishEvent
方法。
自定义监听
MyEventListener
实现了ApplicationListener
接口,在spring
初始化(见上文中的addApplicationListener
)的时候会添加到applicationListeners
中,在执行publishEvent
方法时就会走MyEventListener
中的onApplicationEvent
方法。
客户端
访问127.0.0.1:8008/demo/test
就可以发送广播了,发送与监听内容如下:
看到这儿,相信你己经完全掌握了广播与监听的精髓了,赶快实践起来吧。阿 Q 将持续更新 java 实战方面的文章,如果你有不同的意见或者更好的 idea,欢迎联系阿 Q。
阿 Q 说代码,值得关注的公众号
文章风格多变,配图通俗易懂,故事生动有趣,来聊聊技术呀!
版权声明: 本文为 InfoQ 作者【阿Q说代码】的原创文章。
原文链接:【http://xie.infoq.cn/article/f613ec0d97ae324c6e830028b】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论