原创:微信微信官方账号 [阿Q代码],欢迎分享,请保留转载来源。
最近疫情形势严峻,形势不容乐观,周末也不敢出去浪,躲在家里“葛优躺”。闲着没事,又翻了一遍Spring
源代码。不翻不知道,一翻吓了一跳,之前翻过的源代码已经吃进肚子里了,再见也是陌生人。
个人建议:为了以后快速捡起某个知识点,最好的办法就是形成文档。下次有遗漏的时候,直接看文档,按照之前的想法刷一遍,“干净卫生”。
在之前的文章中,我们已经介绍了如何在项目中快速启动“事件通知机制”,我相信我们已经掌握了它。但我们是高级的javaer
,我们应该知道为什么。今天,我们将从源代码的角度分析广播和监控的底层实现原理。
源码解析源代码导入教程也为您准备好了,不来试试吗?
版本号:spring-framework-5.0.x
为实现广播和监控的功能,Spring
为我们提供了两个重要的函数接口:ApplicationEventPublisher
和ApplicationListener
。前者的publishEvent()
该方法为我们提供了发送广播的能力;onApplicationEvent()
该方法为我们提供了监控和处理事件的能力。
接下来,让我们分析一下spring
这两种能力是如何运用的。
我不知道你是否熟悉单例对象的初始调用过程?主要调用方法如下:
发送广播applyBeanPostProcessorsBeforeInitialization
该方法将遍历工厂创建的所有方法Bean
后置处理器,然后依次执行后置处理器对应postProcessBeforeInitialization
方法。
在这种方法的实现类中,我们看到了两个熟悉的类名
不知道大家还记不记得。这两类是在beanFactory
在准备过程中添加了两个bean
后置处理器,因此这个地方将依次执行这两类的实现方法。
因为蓝框中类的实现方法是默认实现按照原样返回给定的bean
,因此,这里不需要太多的分析。让我们关注实现红框中类的方法。
这种方法中最重要的是invokeAwareInterfaces
该方法的作用是检测相应的方法bean
是否实现了某一点Aware
接口,如果实现了,将进行相关调用。
if (bean instanceof ApplicationEventPublisherAware) { ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);}
我们发现在invokeAwareInterfaces
上述代码出现在方法中,这不是与广播发送有关吗?所以只要我们写一个类来实现它ApplicationEventPublisherAware
接口,你可以在这里bean
中注入一个ApplicationEventPublisher
对象,也获得了发送广播的能力。
applyBeanPostProcessorsAfterInitialization
该工厂创建的所有方法也将通过工厂创建的所有方法进行访问Bean
后置处理器,然后依次执行后置处理器对应postProcessAfterInitialization
方法。
同样,也有实现这种方法的类别ApplicationContextAwareProcessor
和ApplicationListenerDetector
两类,但不同的是,前者类的实现方法是默认实现按原始返回的给定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
有兴趣的可以关注实战文章,也可以来技术群讨论问题!