SpringFramework事件与监听机制(@EventListener)

SpringBoot版本:2.0.2.RELEASE
SpringFramework版本:5.0.6.RELEASE

文章目录

  • @EventListener的初始化工作
  • ApplicationListenerMethodAdapter的结构
  • ApplicationListenerMethodAdapter匹配事件

之前的“SpringFramework事件与监听机制”系列文章已经从接口层面上介绍了事件、监听器和发布器。其实使用@EventListener也能达到ApplicationLister接口的效果,根据我们对spring的认识,它们不会轻易放过代码重用的机会,那么@EventListener标注的使用,是否基于SpringFramework事件与监听机制呢?本文旨在探讨@EventListener是如何工作的,所以@EventListener使用上的细节,请在官网或者其实阅读资料查询。

@EventListener的初始化工作

EventListenerMethodProcessor与DefaultEventListenerFactory是两个重要的类,它们在AnnotationConfigUtils#registerAnnotationConfigProcessors方法里被注册到BeanDefinitionRegistry去了。
在SpringBoot程序的程动过程中,在构造AnnotationConfigApplicationContext的时候会调用此方法。它的方法调用链如下:

SpringApplication.run SpringApplication.createApplicationContext AnnotationConfigApplicationContext() AnnotatedBeanDefinitionReader() AnnotationConfigUtils.registerAnnotationConfigProcessors SpringApplication.run SpringApplication.createApplicationContext AnnotationConfigApplicationContext() AnnotatedBeanDefinitionReader() AnnotationConfigUtils.registerAnnotationConfigProcessors

AnnotationConfigUtils#registerAnnotationConfigProcessors部分代码如下:

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

		....
		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;
	}
  • EventListenerMethodProcessor
    EventListenerMethodProcessor实现了SmartInitializingSingleton接口,因此它重载了afterSingletonsInstantiated方法。实现此接口的目的,为的是在SpringFramwork初始化Bean的整个过程中方法被调用的时机点。我们先大至了解下这方法做什么:
@Override
	public void afterSingletonsInstantiated() {
     
		List<EventListenerFactory> factories = getEventListenerFactories();
		ConfigurableApplicationContext context = getApplicationContext();
		String[] beanNames = context.getBeanNamesForType(Object.class);
		for (String beanName : beanNames) {
     
			if (!ScopedProxyUtils.isScopedTarget(beanName)) {
     
				....
					try {
     
						processBean(factories, beanName, type);
					}
					catch (Throwable ex) {
     
						throw new BeanInitializationException("Failed to process @EventListener " +
								"annotation on bean with name '" + beanName + "'", ex);
					}
				}
			}
		}
	}

大意就是从BeanFactory取出所有已注册类的beanNames, 然后挨个地将bean名字连同EventListenerFactory集合和bean的类型都交给processBean方法处理。

protected void processBean(
			final List<EventListenerFactory> factories, final String beanName, final Class<?> targetType) {
     

		if (!this.nonAnnotatedClasses.contains(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 = getApplicationContext();
				for (Method method : annotatedMethods.keySet()) {
     
					for (EventListenerFactory factory : factories) {
     
						if (factory.supportsMethod(method)) {
     
							Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
							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);
				}
			}
		}
	}

在processBean方法体里,我们看到先把指定类型里面所有使用@EventListener标注的方法取出来,并存放在annotatedMethods变量里。然后经过双重循环,使得annotatedMethods变量里每个方法对象都被每个EventListenerFactory进行处理。EventListenerFactory#createApplicationListener会返回ApplicationListener,该ApplicationListener随后被注册到ConfigurableApplicationContext。
至此,我们虽然没有细看EventListenerFactory的细节,但我们知道此时已经将使用@EventListener标的方法转换为ApplicationListener,并融入到Spring Framework的事件与监听机制当中。
那么,我们来关注下这个过程发生的时机点:

SpringApplication.run SpringApplication.refreshContext SpringApplication.refresh AbstractApplicationContext.refresh AbstractApplicationContext.finishBeanFactoryInitialization ConfigurableListableBeanFactory.preInstantiateSingletons SmartInitializingSingleton.afterSingletonsInstantiated SpringApplication.run SpringApplication.refreshContext SpringApplication.refresh AbstractApplicationContext.refresh AbstractApplicationContext.finishBeanFactoryInitialization ConfigurableListableBeanFactory.preInstantiateSingletons SmartInitializingSingleton.afterSingletonsInstantiated

从SpringBoot的启动过程中发布的事件来看,触发这个过程的时机在ApplicationPreparedEvent事件之后,ContextRefreshedEvent之前。从AbstractApplicationContext#refresh方法体代码可知,这个过程仅仅在发布ContextRefreshedEvent之前发生。

@Override
	public void refresh() throws BeansException, IllegalStateException {
     
		....
				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
		....
	}

此时,AbstractApplicationContext才刚刚添加了Spring Framework的监听器,通过这个过程,补充了使用@EventListener标注监听器,接下来发布ContextRefreshedEvent事件时,SpringBoot监听器、SpringFramework监听器以及用户编程使用@EventListener标注的监听器都会有可能收到(还存在事件与监听器匹配的环节)。后续发布的事件也会传递到它们。

  • DefaultEventListenerFactory
    我们把目光拉回到EventListenerMethodProcessor#afterSingletonsInstantiated的接口方法。该方法会调用getEventListenerFactories方法获取EventListenerFactory接口实现类的集合:
protected List<EventListenerFactory> getEventListenerFactories() {
     
		Map<String, EventListenerFactory> beans = getApplicationContext().getBeansOfType(EventListenerFactory.class);
		List<EventListenerFactory> factories = new ArrayList<>(beans.values());
		AnnotationAwareOrderComparator.sort(factories);
		return factories;
	}

因为DefaultEventListenerFactory是该接口的唯一实现类,所以该方法返回的集合只有一个元素。
DefaultEventListenerFactory重载了EventListenerFactory接口的方法。supportsMethod方法在EventListenerMethodProcessor#processBean方法体内被调用。从代码的上下文来判断,此方法是用来校验DefaultEventListenerFactory是否可以支持使用@EventListener标注的方法监听器。DefaultEventListenerFactory#supportsMethod直接就返回true;createApplicationListener方法是在随后被调用,它使用ApplicationListenerMethodAdapter来封装@EventListener标注的方法监听器:

@Override
	public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
     
		return new ApplicationListenerMethodAdapter(beanName, type, method);
	}

从ApplicationListenerMethodAdapter名字来看,就知道又是采用了适配器模式。那么它的结构以及方法细节,在下节探讨。

ApplicationListenerMethodAdapter的结构

从类的定义来看,它实现了GenericApplicationListener接口,也就是说它会重载supportsEventType方法和supportsSourceType方法 。
我们先观察其构造函数,它有三个形式参数:String beanName, Class targetClass, Method method。这三参数都直接或者经过稍微的处理后存储到相应的属性变量里。然而,我们需要更多的关注它调用的resolveDeclaredEventTypes方法:

private List<ResolvableType> resolveDeclaredEventTypes(Method method, @Nullable EventListener ann) {
     
		int count = method.getParameterCount();
		if (count > 1) {
     
			throw new IllegalStateException(
					"Maximum one parameter is allowed for event listener method: " + method);
		}

		if (ann != null) {
     
			Class<?>[] classes = ann.classes();
			if (classes.length > 0) {
     
				List<ResolvableType> types = new ArrayList<>(classes.length);
				for (Class<?> eventType : classes) {
     
					types.add(ResolvableType.forClass(eventType));
				}
				return types;
			}
		}

		if (count == 0) {
     
			throw new IllegalStateException(
					"Event parameter is mandatory for event listener method: " + method);
		}
		return Collections.singletonList(ResolvableType.forMethodParameter(method, 0));
	}

这条方法有返回值,让人第一时间就想到它是用于获取数据,确实它起到了获取数据的作用,然而它所做的事情还包含@EventListener标注使用的规范:
首先,在参数约束上,方法参数不能多于一个,并且如果@EventListene标注没有指定事件类型,则使用@EventListene标注的方法的形式参数(后文简称“方法参数”)必须得有一个;
其次,@EventListener的value属性的解析优先于方法参数的获取,也就是只要使用@EventListener标注时指定了监听的事件类型,无论是一个还是多个,都不再解析方法参数指定的事件类型;
还有,如果@EventListener没有指定事件类型,可通过方法参数指定事件类型。

构造函数调用resolveDeclaredEventTypes方法获取了这个监听器所支持的事件类型的集合,并存储于declaredEventTypes属性内。
在重载的supportsEventType方法里,declaredEventTypes属性被用于判断参数的类型是否为集合内某个事件类型或者其子类,如果不是的话,进一步检查参数是否为泛型事件PayloadEvent,如果是的话检查泛型的具体类型是否符合;在重载的supportsSourceType方法很简单,直接返回了true。

ApplicationListenerMethodAdapter匹配事件

在EventListenerMethodProcessor#processBean的方法里,@EventListener标注的方法被封装成ApplicationListenerMethodAdapter监听器,并添加到ConfigurableApplicationContext的监听器集合里。当ConfigurableApplicationContext发布事件时,在分组缓存建立前,都会进行事件类型与监听器的匹配。AbstractApplicationEventMulticaster#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));
	}

因为ApplicationListenerMethodAdapter实现了GenericApplicationListener接口,所以无需进一步地封装成GenericApplicationListenerAdapter,直接通过supportsEventType方法和supportsSourceType方法的返回值来判断是否匹配。前文我们发现ApplicationListenerMethodAdapter#supportsSourceType方法直接返回true,所以在使用@EventListener时设置的classes属性或者方法参数的事件类型成为了关键。

你可能感兴趣的:(Java阵营)