当前位置: 首页 > 图灵资讯 > 技术篇> 源码级别的广播与监听实现

源码级别的广播与监听实现

来源:图灵教育
时间:2023-06-01 09:41:26

原创:微信微信官方账号 [阿Q代码],欢迎分享,请保留转载来源。

最近疫情形势严峻,形势不容乐观,周末也不敢出去浪,躲在家里“葛优躺”。闲着没事,又翻了一遍Spring源代码。不翻不知道,一翻吓了一跳,之前翻过的源代码已经吃进肚子里了,再见也是陌生人。

个人建议:为了以后快速捡起某个知识点,最好的办法就是形成文档。下次有遗漏的时候,直接看文档,按照之前的想法刷一遍,“干净卫生”。

在之前的文章中,我们已经介绍了如何在项目中快速启动“事件通知机制”,我相信我们已经掌握了它。但我们是高级的javaer,我们应该知道为什么。今天,我们将从源代码的角度分析广播和监控的底层实现原理。

源代码导入教程也为您准备好了,不来试试吗?

版本号:spring-framework-5.0.x

源码解析

为实现广播和监控的功能,Spring为我们提供了两个重要的函数接口:ApplicationEventPublisherApplicationListener。前者的publishEvent()该方法为我们提供了发送广播的能力;onApplicationEvent()该方法为我们提供了监控和处理事件的能力。

接下来,让我们分析一下spring这两种能力是如何运用的。

我不知道你是否熟悉单例对象的初始调用过程?主要调用方法如下:

源码级别的广播与监听实现_广播

发送广播

applyBeanPostProcessorsBeforeInitialization该方法将遍历工厂创建的所有方法Bean后置处理器,然后依次执行后置处理器对应postProcessBeforeInitialization方法。

在这种方法的实现类中,我们看到了两个熟悉的类名

源码级别的广播与监听实现_监听_02

不知道大家还记不记得。这两类是在beanFactory在准备过程中添加了两个bean后置处理器,因此这个地方将依次执行这两类的实现方法。

源码级别的广播与监听实现_监听_03

因为蓝框中类的实现方法是默认实现按照原样返回给定的bean,因此,这里不需要太多的分析。让我们关注实现红框中类的方法。

这种方法中最重要的是invokeAwareInterfaces该方法的作用是检测相应的方法bean是否实现了某一点Aware接口,如果实现了,将进行相关调用。

if (bean instanceof ApplicationEventPublisherAware) {    ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);}

我们发现在invokeAwareInterfaces上述代码出现在方法中,这不是与广播发送有关吗?所以只要我们写一个类来实现它ApplicationEventPublisherAware接口,你可以在这里bean中注入一个ApplicationEventPublisher对象,也获得了发送广播的能力。

监听消息

applyBeanPostProcessorsAfterInitialization该工厂创建的所有方法也将通过工厂创建的所有方法进行访问Bean后置处理器,然后依次执行后置处理器对应postProcessAfterInitialization方法。

同样,也有实现这种方法的类别ApplicationContextAwareProcessorApplicationListenerDetector两类,但不同的是,前者类的实现方法是默认实现按原始返回的给定bean,后者进行了相关处理。

this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);

将实现上述代码ApplicationListener接口的bean添加到监听器列表中,最终保存在监听器列表中AbstractApplicationEventMulticaster的成员变量defaultRetriever的集合applicationListeners中。

猜测:发送广播信息时,直接找到集合的监听器,然后调用每个监听器onApplicationEvent完成事件处理的方法。

案例分析

refresh()finishRefresh()方法中,

publishEvent(new ContextRefreshedEvent(this));

发送的事件类型为ContextRefreshedEvent用来代表广播新闻的广播新闻Spring容器初始化结束。通过分析发现,该方法的主要代码如下:

///真正的广播交付 applicationEventMulticaster 完成getaplicationentmulticaster().multicastEvent(applicationEvent, eventType);

refresh()initApplicationEventMulticaster()applicationEventMulticaster初始化为SimpleApplicationEventMulticaster

在实现类SimpleApplicationEventMulticaster在注册方法中,会找到注册方法ApplicationListener列表,然后单独调用invokeListener方法(将监控和事件作为参数传输到方法并执行的过程是发送广播的过程)。

底部调用的是listener.onApplicationEvent(event);方法,即各监控实现类单独处理广播新闻的逻辑。

新闻与监控绑定

看到这里,你有没有注意到广播过程中发生了新闻类型和监听器的绑定?接下来,让我们来看看

我们看一下multicastEvent()方法中的getApplicationListeners(event, type)方法。

在这种方法中使用ConcurrentHashMap类型的缓存retrieverCache,因此,每种类型的事件在广播中都会触发绑定操作。它的key是由事件的来源和类型决定的,它的value包含了由事件来源和类型决定的所有监控列表。

绑定逻辑出现在其中retrieveApplicationListeners在方法中,您可以在源码中查看。

实战教学

纸上得来终觉浅,绝对知道这件事要练。为了更好地了解广播和监控的过程,我们当然要用实战来辅助!

自定义事件

public class MyEvent extends ApplicationContextEvent {    public MyEvent(ApplicationContext source) {        super(source);    }}

自定义广播

@Componentpublic class MyPublisher implements ApplicationEventPublisherAware, ApplicationContextAware {private ApplicationEventPublisher applicationEventPublisher;private ApplicationContext applicationContext;@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}///发送广播消息publicccic void publishEvent(){System.out.println(“我要开始发消息了。。。。");MyEvent myEvent = new MyEvent(applicationContext);applicationEventPublisher.publishEvent(myEvent);}}

MyPublisher实现了ApplicationEventPublisherAware接口 ,在spring初始化(见上文invokeAwareInterfaces)会有回调setApplicationEventPublisher方法获得初始化(添加)bean后置处理器ApplicationContextAwareProcessor)时的AbstractApplicationContext,而AbstractApplicationContext又间接实现了ApplicationEventPublisher并获得发送能力。真正执行的是 AbstractApplicationContext 类中的 publishEvent 方法。

自定义监听

@Componentpublic class MyEventListener implements ApplicationListener<MyEvent> {    @Override    public void onApplicationEvent(MyEvent event) {        System.out.println(“我听到了你的消息”);    }}

MyEventListener实现了ApplicationListener接口,在spring初始化(见上文addApplicationListener)添加时会添加applicationListeners中,在执行publishEvent 方法的时候会走MyEventListener中的onApplicationEvent方法。

客户端

@RestController@RequestMapping("/demo")public class DemoTest {    @Autowired    private MyPublisher myPublisher;    @RequestMapping("/test")    public void test() {        myPublisher.publishEvent();    }}

访问127.0.0.1:8008/demo/test广播可以发送,发送和监控内容如下:

我要开始发消息了。。。我听到了你的消息

看到这里,我相信你已经完全掌握了广播和监控的本质。快点练习。阿Q将继续更新java有兴趣的可以关注实战文章,也可以来技术群讨论问题!