Spring 事件机制源码分析

232 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情

前言

在学习Spring的过程中,一定是一个漫长且又艰难的过程,因为我们所有技术的沿用的框架大差不差都是基于Spring的延伸,其底层都是基于Spring的各种机制,所以学习Spring一定要有耐心,看一遍不懂,就看两遍,两遍不懂就往三四遍看,并且自己去记录学习笔记,无论是本地记录还是同步到博客上,我都觉得是一种进步,我最近也正在进行Spring的学习,废话不多说,让我们来看看Spring事件机制具体是怎么实现的。

Spring事件机制

关于事件机制主要分为三部分,首先是是定义事件、然后进行发布事件、在对发布的事件进行监听,获取到事件发布的消息。

定义事件

ApplicationEvent,该类属于spring-context,jar包下的定义事件的抽象基类,如果想自定义事件,只需继承这个基类即可。

A652D25F-4B08-4A7C-9748-974F3443DBB2.png

发布事件

ApplicationEventPublisher 紧接着看这个接口,看接口名可以知道是推送事件的。

2D69619B-6C4F-4E82-B374-FD6A2B715884.png

默认的publishEvent方法,入参是一个ApplicationEvent对象,要发送的也就是定义的这个事件。

根据代码的追踪,找到了onApplicationEvent方法,看到了Spring常用的ApplicationContext初始化上下文的方法,发送事件是依靠ApplicationContext初始化上下文的方式。

4F514EAD-DEFE-4196-87DB-312C8132DE7C.png

61DC81F9-07C9-43E1-9D97-4ABBD8EC1493.png

C6CA660E-AB6C-4CA2-AA57-5737F0839845.png

所有需要发布的事件ApplicationEvent,都存放在ConcurrentLinkedQueue 线 程安全非阻塞队列里,入列的方式也是遵循先进先出。

监听事件

38FAE2C5-D9E9-494E-9158-0DE1DC1DF499.png

通过代码追踪找到了publishEvent的实现方法,看代码也是将applicationEvent事件存放在set集合里

taskExecutor异步处理

这里看到如果集合里面的事件不为空,就会交给ApplicationEventMulticaster类处理,既然是事件的监听必然离不开Listener,multicastEvent方法将接收到的事件,通过泛型的方式接收事件类型。

这里可以看创建了Executor线程池,如果线程池存在通过线程池异步去监听事件,反之则是同步监听事件。

7C5F2542-E533-4503-89F1-C84311751AA8.png

最后调用监听器的onApplicationEvent方法进行处理

4AF687B8-C5DF-45D1-88D3-3A0F8D139C69.png

关于监听器是如何匹配事件的,我们接着看 getApplicationListeners这个方法入参为(event,type), retrieveApplicationListeners核心方法,跟踪进去会看到supportsEvent方法,先去 匹配监听器类型再去判断监听器是否匹配对应的事件类型

//以上代码省略
// No ListenerRetriever caching -> no synchronization necessary
return retrieveApplicationListeners(eventType, sourceType, null);


//retrieveApplicationListeners方法

for (ApplicationListener<?> listener : listeners) {
	if (supportsEvent(listener, eventType, sourceType)) {
		if (retriever != null) {
			retriever.applicationListeners.add(listener);
		}
		allListeners.add(listener);
	}
}

//supportsEvent 方法

protected boolean supportsEvent(
		ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {

	GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
			(GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
	return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
}

AbstractApplicationContext

前面说到ApplicaitonContext去初始化ApplicaitonEvent事件,后面说到ApplicationListener监听发送的事件,现在看refresh方法,也是整个事件从初始化、注册、发布核心的方法,监听器的注册就是发生在registerListeners方法里面。

public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
	// 准备刷新上下文
	prepareRefresh();

	// 告诉子类刷新内部bean工厂。
	ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

	// 准备在此上下文中使用的bean工厂。
	prepareBeanFactory(beanFactory);

	try {
		// 允许在上下文中对bean工厂进行后处理
		postProcessBeanFactory(beanFactory);

		// 调用在上下文中作为bean注册的工厂处理器
		invokeBeanFactoryPostProcessors(beanFactory);

		// 注册拦截bean创建的bean处理器。
		registerBeanPostProcessors(beanFactory);

		// 初始化此上下文的消息源。
		initMessageSource();

		// 为此上下文初始化事件多播程序。
		initApplicationEventMulticaster();

		// 初始化特定上下文子类中的其他特殊bean。
		onRefresh();

		// 检查监听器bean并注册它们。
		registerListeners();

		// 实例化所有剩余的(非lazy-init)单例。
		finishBeanFactoryInitialization(beanFactory);

		// 发布相应的事件。
		finishRefresh();
	}

B0475D75-7E19-4120-A48E-C3CBDFC2C042.png

总结

Spring的Event事件机制,在很多场景都会有体现,包括注册中心的热部署、发布—订阅模式的实现等…并且支持异步和同步的方式对事件进行监听,也可以通过注解的方式@EventListener对定义的事件进行监听。