细谈Spring的事件监听机制

一、事件监听的基本原理

事件监听机制和发布订阅机制是很相似的:发布了一个事件后,监听该类型事件的所有监听器会触发相应的处理逻辑。正如Spring官方文档上所说的,整个上就是一个观察者的模式。那么,我们不妨猜测下,Spring是如何来实现事件发布监听的:盲猜底层使用一个集合存储了所有的监听器,当发布了一个事件后,遍历事件监听器集合,然后过滤符合事件类型的监听器,最后触发相应的事件处理逻辑。

二、Spring中事件监听的实现

1.Spring中事件监听的相关规范

在Spring中,事件监听机制主要涉及到了一下几个关键的规范(抽象类及接口):ApplicationEvent、ApplicationListener、ApplicationEventPublisher,我们来分析一下这几个规范吧:

  1. ApplicationEvent: 同样,所以说Spring的事件是符合jdk的规范的,这个抽象类继承了jdk内置的事件规范类EventObject(即jdk建议所有的事件都继承EventObject这个类)。ApplicationEvent是Spring家的事件规范。所以我们在自定义事件的时候,可以继承与ApplicationEvent,比如,Spring家自己关于容器上下文事件就又定义了一个容器上下文的时间规范ApplicationContextEvent,它同样是继承于ApplicationEvent的,只不过扩充了获取发出事件容器的方法;今后,我们同样可以在继承于ApplicationEvent的基础上定义自己的事件规范。
  2. ApplicationListener:这是一个函数式接口,同样时事件监听器的规范,当监听到自己监听的事件类型时就会调用onApplicationEvent方法来执行监听逻辑
  3. ApplicationEventPublisher:这同样是一个函数式接口,定义了事件发布的规范,任何的事件发布器ApplicationEventPublisher都是通过调用publishEvent来进行事件的发布

在了解了以上的三个规范后,我们再来看一看上面三个规范在Spring底层的一些详细细节点:

  1. 在Spring的底层事件可以大致分为两种:一种是ApplicationEvent,另一种是PayloadApplicationEvent,虽然PayloadApplicationEvent也是继承于ApplicationEvent,但是我们得把它单独拿出来,因为PayloadApplicationEvent其实就是我们发布一个Object类型的事件后Spring底层会帮我们把其封装成PayloadApplicationEvent,其中Object的事件信息被封装到PayloadApplicationEvent中的payload属性中。
  2. 这里重点说一下ApplicationListener在Spring不同版本中的演进:
    1. Spring Framework 3.0 之前 的ApplicationListener是不支持泛型的,所以每个监听器都会收到所有的事件,如果需要过滤不同类型的事件的话,则需要借助instanceof来进行筛选
    2. Spring Framework 3.0 开始,Application支持ApplicationEvent泛型监听,监听具体的事件,无需借助instanceof进行筛选
    3. 但是引入泛型会引发另一个问题,如果我想同时监听多个类型的事件怎么办呢?如果继续使用ApplicationListener就又回到了Spring Framework 3.0之前了,为此,Spring Framework 3.0引入了SmartApplicationListener接口;该接口通过supports* 方法过滤需要监听的ApplicationEvent类型和事件源类型,从而达到监听不同类型的ApplicationEvent的目的
  3. 关于ApplicationEventPublisher我们提一个关键点,那就是SpringIOC容器的顶级接口ApplicationContext是实现了ApplicationEventPublisher接口的,那就意味着Spring容器就是一个事件发布器,并且在AbstractApplicationContext中,对时间发布器中的publishEvent方法进行了逻辑重写,Spring的事件发布逻辑正是在此处。所以,我们拿到Spring的容器,我们就能发布事件了
2.Spring中事件监听的流程

在进行探索整个事件发布的流程之前,我们先编写一个事件发送与监听的代码:
自定义监听器代码:

		// 该监听器监听String类型的事件
		@Component
 	public class MyInterfaceListener implements ApplicationListener<PayloadApplicationEvent<String>> {
 		@Override
 		public void onApplicationEvent(PayloadApplicationEvent<String> event) {
     		System.out.println("我就监听到了:" + event.getSource().getClass().getSimpleName() + "发送的事件,事件的内容为:" + event.getPayload());
 		}
 	}

发送端的代码:

		    @Autowired
 		private ApplicationContext applicationContext;
 		@GetMapping("/publish")
 		public void publish() {
     		applicationContext.publishEvent("我是一个事件");
 		}

当我请求publish的请求时,控制台会打印如下

我就监听到了:AnnotationConfigServletWebServerApplicationContext发送的事件,事件的内容为:我是一个事件

案例跑完后,我们来看一下publishEvent中整个事件发布的流程,首先我们进入到AbstractApplicationContext中来查看Spring底层对publishEvent的重写逻辑,代码如下:

	protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
 	Assert.notNull(event, "Event must not be null");

 	// Decorate event as an ApplicationEvent if necessary
 	ApplicationEvent applicationEvent;
 	if (event instanceof ApplicationEvent) {
 		applicationEvent = (ApplicationEvent) event;
 	}
 	else {
 		applicationEvent = new PayloadApplicationEvent<>(this, event);
 		if (eventType == null) {
 			eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
 		}
 	}

 	// Multicast right now if possible - or lazily once the multicaster is initialized
 	if (this.earlyApplicationEvents != null) {
 		this.earlyApplicationEvents.add(applicationEvent);
 	}
 	else {
 		getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
 	}

 	// Publish event via parent context as well...
 	if (this.parent != null) {
 		if (this.parent instanceof AbstractApplicationContext) {
 			((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
 		}
 		else {
 			this.parent.publishEvent(event);
 		}
 	}
 }

在这里我们就看到了,Spring底层对ApplicationEvent事件和PayloadApplicationEvent事件的处理:如果发布的直接就是ApplicationEvent类型的事件,那么就直接转换成ApplicationEvent类型,而如果不是ApplicationEvent类型的事件,那么就是我们所说的Object类型的事件,就会帮我们封装成PayloadApplicationEvent,并将Object类型的事件信息存储到payload属性中。在处理完事件的类型后,执行了下面这行重要的代码:

		getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);

其中getApplicationEventMulticaster方法是拿到容器中的事件广播器,然后通过这个事件广播器来进行事件的广播。那么这个事件多播器我们也没有配置创建,是怎么获取的呢?这就需要看一下Spring容器刷新的9大步当中的initApplicationEventMulticaster初始化事件广播器的方法了,如下:

	protected void initApplicationEventMulticaster() {
 	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
 	if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
 		this.applicationEventMulticaster =
 				beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
 		if (logger.isTraceEnabled()) {
 			logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
 		}
 	}
 	else {
 		this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
 		beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
 		if (logger.isTraceEnabled()) {
 			logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
 					"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
 		}
 	}
 }

一看代码,还是Spring的老技巧,有了就用你的,没有我就帮你做一个然后用我的:首先,Spring会查看容器中有没有名称为applicationEventMulticaster的bean对象,如果有的话,就返回这个对象;如果没有的话,Spring就会帮我们new一个SimpleApplicationEventMulticaster的事件广播器对象返回。所以,我们默认就是走的SimpleApplicationEventMulticaster中的multicastEvent事件广播逻辑,代码如下:

	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
 	ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
 	Executor executor = getTaskExecutor();
 	for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
 		if (executor != null) {
 			executor.execute(() -> invokeListener(listener, event));
 		}
 		else {
 			invokeListener(listener, event);
 		}
 	}
 }

看到这里也就验证了我们刚开始的猜测,Spring正式通过拿到容器中所有符合当前事件的监听器,然后循环遍历挨个调用onApplicationEvent方法。

三、@EventListener的实现

除了实现ApplicationListener接口的方式来实现监听器,在走向注解驱动开发后,Spring同样也为我们提供了@EventListener这个注解来实现监听器,注解使用起来还是十分的方便的:只需要在Spring托管Bean的public方法上加上@EventListener注解就可以监听事件了,想监听的事件类型也可以通过注解的属性来进行配置,例如像这样:就只会监听容器刷新的ContextRefreshedEvent事件。

 /**
  * 监听ioc 容器刷新完毕事件
  * @param event
  */
 @EventListener({ContextRefreshedEvent.class})
 public void listen(ContextRefreshedEvent event) {
     //
     System.out.println(String.format(message,"ContextRefreshedEvent",event.getSource()));
 }

@EventListener是为了支持注解开发,在Spring Framework 4.2的时候才新增的。我们先不看Spring底层是如何实现的,如果说现在让我们来开发一个注解来实现事件监听的功能,那你会怎么做呢,如果开始我们和Spring家想的一样:只需要在Spring托管Bean的public方法上加上@EventListener注解就可以监听事件了,而我们又知道Spring的底层是循环遍历支持该事件的监听器集合来进行广播事件的,那么现在为了让我们这个注解也能达到监听事件的效果,即也能被广播到,那么我觉得我们可以想到两种方式来实现:

  1. 新建一个集合,将所有标有@EventListener方法的bean都放到这个集合里面,当进行事件广播的时候,同时也循环遍历这个集合,来触发可以监听这个事件的监听器
  2. 使用适配器模式,将标有@EventListener注解的方法适配成ApplicationListener(其实,全类名,以及方法名,方法参数都有了,就可以通过反射来执行到适配之前的方法了),然后一起放到Spring储存事件监听器的集合中去

当然,你们可能会想到更好的方法,希望评论区一起讨论啊。
那么,Spring的底层是采用什么的方式来实现@EventListener能够进行监听的呢?其实,Spring家就是使用第二种适配器模式来完成的,既然我们知道了Spring是使用这种方式实现的,那么我们继续再想想需要在Spring Framework 3.0版本的基础上增加哪些组件呢:

  1. 我们需要找到所有标有@EventListener方法的bean,其实,就这一问题的解决方案有很多,主要看是在每个bean实例化的过程中就行判断还是说等所有的bean都实例化完了,在最后一起就行判断,而Spring的底层是选择了后者,通过新增一个实现了SmartInitializingSingleton接口的EventListenerMethodProcessor,我们知道在容器刷新快结束的时候,也就是所有的bean都实例化完成之后,Spring会挨个调用SmartInitializingSingleton的afterSingletonsInstantiated方法,Spring也正是在此进行处理标有@EventListener的bean的
  2. 另外,就是得提供一个适配器,来完成@EventListener注解到ApplicationListener的适配,所以,在Spring Framework 4.2的版本中,引入了ApplicationListenerMethodAdapter这个适配类
  3. 为了方便构造出ApplicationListenerMethodAdapter这个适配器对象,Spring还新增了一个EventListener工厂类DefaultEventListenerFactory来帮助我们构建ApplicationListenerMethodAdapter对象,或许这就是优秀代码人的代码能力体现吧,是我的话,可能就直接自己new了

上面已经说明了新增了哪些组件了,以及需要新增的原因,下面我们看看EventListenerMethodProcessor以及DefaultEventListenerFactory这两个组件是什么时候注入到容器中的,在容器构造函数中,创建AnnotatedBeanDefinitionReader对象的时候调用registerAnnotationConfigProcessors方法进行了注入,代码如下:

		public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
 		BeanDefinitionRegistry registry, @Nullable Object source) {

 	DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
 		// ......此处.省略了其他无关代码
 	if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
 		RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
 		def.setSource(source);
 		beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
 	}

 	if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
 		RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
 		def.setSource(source);
 		beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
 	}

 	return beanDefs;
 }
看着两个if写在最后,我们就猜得到是后期为了支持注解@EventListener而后加上去的,只要我们自己没有注入名字为org.springframework.context.event.internalEventListenerProcessor以及名字为org.springframework.context.event.internalEventListenerFactory的bean定义信息的时候,Spring就会帮我们注入EventListenerMethodProcessor以及DefaultEventListenerFactory这两个类型的bean定义信息,从而在后期帮忙实现@EventListener监听的功能,下面就来大致看看具体是如何实现的吧:
首先来看看EventListenerMethodProcessor的afterSingletonsInstantiated方法:
```java
	public void afterSingletonsInstantiated() {
	ConfigurableListableBeanFactory beanFactory = this.beanFactory;
	Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
	// 拿到容器中所有对象的名字
	String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
	for (String beanName : beanNames) {
		//...此处省略了不必要的代码
				try {
					// 遍历名字进行处理
					processBean(beanName, type);
				}
				catch (Throwable ex) {
					throw new BeanInitializationException("Failed to process @EventListener " +
							"annotation on bean with name '" + beanName + "'", ex);
				}
			}
		}
```

processBean的代码逻辑

	private void processBean(final String beanName, final Class<?> targetType) {
		if (!this.nonAnnotatedClasses.contains(targetType) &&
				AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
				!isSpringContainerClass(targetType)) {

			Map<Method, EventListener> annotatedMethods = null;
			try {
				annotatedMethods = MethodIntrospector.selectMethods(targetType,
						(MethodIntrospector.MetadataLookup<EventListener>) method ->
								AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
			}
			catch (Throwable ex) {
				// An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.
				if (logger.isDebugEnabled()) {
					logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex);
				}
			}

			if (CollectionUtils.isEmpty(annotatedMethods)) {
				this.nonAnnotatedClasses.add(targetType);
				if (logger.isTraceEnabled()) {
					logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());
				}
			}
			else {
				// Non-empty set of methods
				ConfigurableApplicationContext context = this.applicationContext;
				Assert.state(context != null, "No ApplicationContext set");
				List<EventListenerFactory> factories = this.eventListenerFactories;
				Assert.state(factories != null, "EventListenerFactory List not initialized");
				// 遍历所有包含@EventListener注解的方法
				for (Method method : annotatedMethods.keySet()) {
					// 拿到容器中所有的EventListenerFactory
					for (EventListenerFactory factory : factories) {
						if (factory.supportsMethod(method)) {
							Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
							// 创建出ApplicationListenerMethodAdapter对象
							ApplicationListener<?> applicationListener =
									factory.createApplicationListener(beanName, targetType, methodToUse);
							if (applicationListener instanceof ApplicationListenerMethodAdapter) {
								((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
							}
							// 将适配后的对象加入到监听器集合中
							context.addApplicationListener(applicationListener);
							break;
						}
					}
				}
				if (logger.isDebugEnabled()) {
					logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
							beanName + "': " + annotatedMethods);
				}
			}
		}
	}

根据上面的代码,我们了解了Spring底层的处理逻辑是:首先拿到容器中的所有bena对象的名称,然后遍历没一个对象进行判断它是否有标有@EventListener注解的方法,有的话就存储起来,最后来遍历具有@EventListener注解的方法的bean,通过适配器工厂EventListenerFactory将其适配为ApplicationListener对象,最后放入到监听器集合中。你看是不是和我们前面想的方案二是一样的所以说,当我们掌握了Spring的扩展点后,自己来完成一些新的功能也是没有问题的。

最后

有任何问题,欢迎和小松子一起讨论啊,Spring家族爱好者一枚,希望你也是

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值