SpringFramework事件与监听机制(监听器)

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


SpringFramework的监听者这一块设计得也是挺有意思的。监听者的核心设计思想就是通知者模式。随着SpringFramework版本的不断演变,整个事件与监听机制都在不断的优化。监听者可以仅接受自己感兴趣的事件。SpringBoot是基于SpringFramework发展而来的,它的监听者也是融入到SpringFramework的机制里。
在这里插入图片描述
上图是SpringFramework监听者接口方面的UML图。除了EventListener接口外,ApplicationListener、GenericApplicationListener和SmartApplicationListener接口都有自己的实现类。那么,划分不同的接口背后想表达什么意思呢?后面我们逐步解开这个迷。

监听者从何而来

在SpringBoot的工程里,SpringFramework的监听者有两部分来源,一些SpringBoot带来的,别一些是SpringFramework原生。

来自SpringBoot的监听器

在《SpringFramework事件与监听机制(事件)》文章里,我们看到在SpringBoot的启停过程中,SpringBoot的事件是最早发布的,到了中间的部分才发布ContextRefreshedEvent。那么,是不是SpringBoot的事件发布给SpringBoot的监听者,Spring的事件发布给Spring的监听者呢?我们从代码的层面来观察。
根据《SpringBoot事件与监听机制》,SpringBoot通过EventPublishingRunListener发布事件,我们把目光放在EventPublishingRunListener的几个发布事件的方法。它的方法的顺序跟SpringBoot发布各阶段事件的顺序是一致的。
从代码看,SpringBoot发布ApplicationStartingEvent和ApplicationEnvironmentPreparedEvent都有着相同的逻辑:

@Override
	public void starting() {
    
    
		this.initialMulticaster.multicastEvent(
				new ApplicationStartingEvent(this.application, this.args));
	}

	@Override
	public void environmentPrepared(ConfigurableEnvironment environment) {
    
    
		this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
				this.application, this.args, environment));
	}

在发布ApplicationPreparedEvent的contextLoaded方法就有些不一样了:

@Override
	public void contextLoaded(ConfigurableApplicationContext context) {
    
    
		for (ApplicationListener<?> listener : this.application.getListeners()) {
    
    
			if (listener instanceof ApplicationContextAware) {
    
    
				((ApplicationContextAware) listener).setApplicationContext(context);
			}
			context.addApplicationListener(listener);
		}
		this.initialMulticaster.multicastEvent(
				new ApplicationPreparedEvent(this.application, this.args, context));
	}

它会先将SpringBoot的监听器加到SpringFramework的context里。然后才发布ApplicationPreparedEvent事件。

后面的ApplicationStartedEvent和ApplicationReadyEvent都是通过SpringFramework的ConfigurableApplicationContext来发布。

@Override
	public void started(ConfigurableApplicationContext context) {
    
    
		context.publishEvent(
				new ApplicationStartedEvent(this.application, this.args, context));
	}

	@Override
	public void running(ConfigurableApplicationContext context) {
    
    
		context.publishEvent(
				new ApplicationReadyEvent(this.application, this.args, context));
	}

在这里,我们至少看到一个发布者身份转换的过程。而转换的准备阶段就在发布ApplicationPreparedEvent的时候,此时将SpringBoot的监听器都“告诉”SpringFramework的ConfigurableApplicationContext了。用“告诉”这词,因为SpringBoot自己仍然知道监听者的地址,而不是说交给SpringFramework后自己就完全不管了。

我们得往回看下代码,在发布ApplicationPreparedEvent的时候是处于什么阶段。SpringApplication#run方法代码片段如下:

public ConfigurableApplicationContext run(String... args) {
    
    
....
	context = createApplicationContext();
	exceptionReporters = getSpringFactoriesInstances(
			SpringBootExceptionReporter.class,
			new Class[] {
    
     ConfigurableApplicationContext.class }, context);
	prepareContext(context, environment, listeners, applicationArguments,
			printedBanner);
	refreshContext(context);
....

prepareContext方法里就会发布ApplicationPreparedEvent事件。从方法的名字来看,就是为context做准备。而这方法的上两条命令ConfigurableApplicationContext才被创建。这方法后面紧跟着就是refreshContext方法,在该方法里,最终是由SpringFramework的ConfigurableApplicationContext发布ContextRefreshedEvent事件。也就是说,在context的准备阶段会做一些事情并且知到了SpringBoot的监听器,将它们放进自己的库里。后续ConfigurableApplicationContext发布的事件也会被SpringBoot的监听者监听到。
SpringBoot后面发布ApplicationStartedEvent和ApplicationReadyEvent的事件虽然表面上通过SpringApplicationRunListeners发布,实质上最终由ConfigurableApplicationContext发布。简单地说,在发布ApplicationPreparedEvent时ConfigurableApplicationContext还未成熟,所以此时没有让它发布事件。到了ContextRefreshedEvent时ConfigurableApplicationContext已经作好了准备(ConfigurableApplicationContext#refresh方法已完成),所以就让它来发布。后来的事件也都由ConfigurableApplicationContext来发布了。
上面的论述有点啰嗦,但结论就是并非一直是SpringBoot发布的事件就只有SpringBoot的监听者收到,SpringFramework发布的事件就只有SpringFramework的监听者收到。事实上是从ContextRefreshedEvent事件开始,ConfigurableApplicationContext发布的事件也能够被SpringBoot的监听者收到。

来自SpringFramework的监听器

AbstractApplicationContext,是众多的ApplicationContext子类的父类,而它实现了ConfigurableApplicationContext接口,接口的refresh方法在AbstractApplicationContext已实现,如果子类没重载就继承了它的refresh方法。目前看SpringFramework的其他Context子类没重载这条方法。
在AbstractApplicationContext#refresh方法有prepareBeanFactory(beanFactory)这一条命令,我们再追踪该命令:

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    
    
....
// Register early post-processor for detecting inner beans as ApplicationListeners.
		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
....

ApplicationListenerDetector实现了DestructionAwareBeanPostProcessor接口,而这接口又继承了BeanPostProcessor接口,因此ApplicationListenerDetector需要实现这些接口声明的方法。而这些接口的方法是由SpringFramework的BeanFactory回调的。

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    
    
	if (bean instanceof ApplicationListener) {
    
    
		// potentially not detected as a listener by getBeanNamesForType retrieval
		Boolean flag = this.singletonNames.get(beanName);
		if (Boolean.TRUE.equals(flag)) {
    
    
			// singleton bean (top-level or inner): register on the fly
			this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
		}
....

通过BeanPostProcessor#postProcessAfterInitialization的注释大概知道这方法被回调的时机是SpringFramework容器内的一些Bean刚被初始化的时候。不管怎么说,当程序运行到此时ConfigurableApplicationContext的实现类已经被创建了,所以通过其addApplicationListener方法添加那些实现了ApplicationListener接口的类的对象。我们再回顾下调用顺序:

SpringApplication AbstractApplicationContext ConfigurableListableBeanFactory run refreshContext refresh refresh prepareBeanFactory addBeanPostProcessor(new ApplicationListenerDetector(this) SpringApplication AbstractApplicationContext ConfigurableListableBeanFactory

监听器监听的事件

监听器实现的接口

监听器沿用了通知者模式,而且它进一步地控制着监听者所能接受事件的范围。监听器的UML类图如下:
在这里插入图片描述ApplicationListener是继承于java原生的EventListener接口,并且ApplicationListener使用了泛型--ApplicationEvent,而ApplicationEvent本身也是接口。这至少说明ApplicationListener接口的实现类需要指定ApplicationEvent的实现类型。其目的就是在类定义的层面确定监听器与事件的关系。另外,图中有一些指向接口的箭头,箭头的另一端代表该接口的实现类,它们有的是作为ApplicationListener的直接实现者,有的是实现了ApplicationListener的某一个子接口或者两个子接口。接下来,我们将探讨泛型是如何实现消息过滤的作用,以及实现ApplicationListener接口和实现该接口的子类会有什么不同。

本部分与《SpringBoot事件与监听机制》的“核心的事件发布者”部分相关。在该文章里我曾称SimpleApplicationEventMulticaster为核心的事件发布器。当它要发布事件时,会获取事件类型和事件源类型相匹配的Listener,然后向这些Listener发布事件。SimpleApplicationEventMulticaster#multicastEvent(ApplicationEvent) 方法是发布事件的入口方法 ,跟踪调用链,我们会来到AbstractApplicationEventMulticaster#getApplicationListeners(
ApplicationEvent , ResolvableType )。该方法会调用retrieveApplicationListeners函,该函数更核心的调用命令是supportsEvent,该方法用于判断指定的Listener是否与事件类型以及事件源类型相匹配:

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));
	}

在此我们先列出这些接口实现类的代表,后续我们分别将它们代入这条函数,观察supportsEvent最终会得到什么样的结果:

实现类 实现的接口
AnsiOutputApplicationListener ApplicationListener
ClasspathLoggingApplicationListener GenericApplicationListener
GenericApplicationListenerAdapter GenericApplicationListener,SmartApplicationListener
SourceFilteringListener GenericApplicationListener,SmartApplicationListener
ConfigFileApplicationListener SmartApplicationListener

我们可以先初步观察这条函数的语义:
首先,判断指定的Listener是否实现了GenericApplicationListener,如果是则转换成GenericApplicationListener类型;如果不是,该Listener由GenericApplicationListenerAdapter进行封装。而GenericApplicationListenerAdapter实现了GenericApplicationListener接口和SmartApplicationListener接口。也就是说直接实现了ApplicationListener接口或者SmartApplicationListener接口的Listener都被GenericApplicationListenerAdapter进行封装。GenericApplicationListenerAdapter其实就是运用了适配器模式,将不是GenericApplicationListener接口的Listener适配成GenericApplicationListener接口的实现,目的是后续统一调用。
然后,经过上一步的处理后,以GenericApplicationListener接口的实现类的角度,通过调用接口声明的supportsEventType方法和supportsSourceType方法来得到Listener是否与事件的类型以及事件源类型匹配的结果。

GenericApplicationListenerAdapter的定义

public GenericApplicationListenerAdapter(ApplicationListener<?> delegate) {
    
    
		Assert.notNull(delegate, "Delegate listener must not be null");
		this.delegate = (ApplicationListener<ApplicationEvent>) delegate;
		this.declaredEventType = resolveDeclaredEventType(this.delegate);
	}

在构造函数里,除了将被适配的Listener作为delegate属性外,还计算出该Listener的泛型(即支持的事件类型),并保存到declaredEventType属性。构造函数里调用的私有方法resolveDeclaredEventType会调用域为包范围的同名函数:

@Nullable
	static ResolvableType resolveDeclaredEventType(Class<?> listenerType) {
    
    
		ResolvableType resolvableType = ResolvableType.forClass(listenerType).as(ApplicationListener.class);
		return (resolvableType.hasGenerics() ? resolvableType.getGeneric() : null);
	}

因为它的作用域为包范围,所以我们可以在自己的测试工程里创建跟它一样的包,然后添加测试类来试试这条函数。
接下来看两条重要的函数:

@Override
public boolean supportsEventType(ResolvableType eventType) {
    
    
		if (this.delegate instanceof SmartApplicationListener) {
    
    
			Class<? extends ApplicationEvent> eventClass = (Class<? extends ApplicationEvent>) eventType.resolve();
			return (eventClass != null && ((SmartApplicationListener) this.delegate).supportsEventType(eventClass));
		}
		else {
    
    
			return (this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType));
		}
	}

@Override
	public boolean supportsSourceType(@Nullable Class<?> sourceType) {
    
    
		return !(this.delegate instanceof SmartApplicationListener) ||
				((SmartApplicationListener) this.delegate).supportsSourceType(sourceType);
	}

下一节,我们分别将实现不同接口的Listener 代表代入AbstractApplicationEventMulticaster#supportsEvent(ApplicationListener<?>, ResolvableType , @Nullable Class<?> )方法进行观察。

实现ApplicationListener接口的Listener

我们以AnsiOutputApplicationListener为代表进行观察。在AbstractApplicationEventMulticaster#supportsEvent函数里,因为它不是GenericApplicationListener的实现类,所以它被GenericApplicationListenerAdapter封装。

  • 在执行GenericApplicationListenerAdapter#supportsEventType方法时,因为它并不实现SmartApplicationListener接口,所以就用它的泛型与事件类型作比较。
public class AnsiOutputApplicationListener
		implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered 

如果事件类型是它支持的ApplicationEnvironmentPreparedEvent一样或者是它的子类,那么此时就返回true,否则返回false。

  • 在执行GenericApplicationListenerAdapter#supportsSourceType函数里,因为它不是SmartApplicationListener接口的实现类,所以直接返回true。
    所以,对于直接实现ApplicationListener接口的Listener,主要考查的是它声明的泛型与事件的类型是否匹配即可。只要事件类型与声明的泛型一样或者是泛型的子类级别,都算是匹配,这种情况下AbstractApplicationEventMulticaster#supportsEvent函数返回true。

实现GenericApplicationListener接口的Listener

我们以ClasspathLoggingApplicationListener为代表进行观察。在AbstractApplicationEventMulticaster#supportsEvent函数里,因为它GenericApplicationListener的实现类,所以只需转换为GenericApplicationListener类型。
ClasspathLoggingApplicationListener#supportsEventType方法如下:

@Override
	public boolean supportsEventType(ResolvableType resolvableType) {
    
    
		Class<?> type = resolvableType.getRawClass();
		if (type == null) {
    
    
			return false;
		}
		return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(type)
				|| ApplicationFailedEvent.class.isAssignableFrom(type);
	}

只要事件类型为ApplicationEnvironmentPreparedEvent或者ApplicationFailedEvent及他们的子类即为匹配。
ClasspathLoggingApplicationListener#supportsSourceType方法如下:

@Override
	public boolean supportsSourceType(Class<?> sourceType) {
    
    
		return true;
	}

直接就返回ture。
所以对于ClasspathLoggingApplicationListener来说,AbstractApplicationEventMulticaster#supportsEvent函数是否返回真值取决于事件是否为ApplicationEnvironmentPreparedEvent或者ApplicationFailedEvent及他们的子类,如果是则为true,否则就false。
通过以上的分析可以让我们有所启发,如果某个实现了GenericApplicationListener接口的Listener,关键在于supportsEventType方法的实现。这种方式的灵活性在于可以让Listener不再限于单个事件类型,是否需要匹配事件源类型貌似不太重要。

实现GenericApplicationListener接口和SmartApplicationListener接口的Listener

我们以SourceFilteringListener为代表进行观察。在AbstractApplicationEventMulticaster#supportsEvent函数里,因为它是GenericApplicationListener的实现类,所以不需要经过GenericApplicationListenerAdapter的封装。
SourceFilteringListener稍有些特别,它同样有适配器的影子。它的构造函数如下:

public SourceFilteringListener(Object source, ApplicationListener<?> delegate) {
    
    
		this.source = source;
		this.delegate = (delegate instanceof GenericApplicationListener ?
				(GenericApplicationListener) delegate : new GenericApplicationListenerAdapter(delegate));
	}
protected SourceFilteringListener(Object source) {
    
    
		this.source = source;
	}

我们关注第一条构造函数,因为它最为复杂而且作用域为public。可以看出,它的作用与GenericApplicationListenerAdapter相似。
它的supportsEventType和supportsSourceType方法如下:

@Override
	public boolean supportsEventType(ResolvableType eventType) {
    
    
		return (this.delegate == null || this.delegate.supportsEventType(eventType));
	}

	@Override
	public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
    
    
		return supportsEventType(ResolvableType.forType(eventType));
	}

	@Override
	public boolean supportsSourceType(@Nullable Class<?> sourceType) {
    
    
		return (sourceType != null && sourceType.isInstance(this.source));
	}

supportsEventType返回值取决于被代理对象的supportsEventType返回值,而supportsSourceType取决于自己支持的事件源类型是否为作为参数的事件源类型或者父类型。
虽然SourceFilteringListener的构造函数有一些特殊,但我们依然可以得出此结论,实现了GenericApplicationListener接口和SmartApplicationListener接口的Listener可以匹配多个事件,是否需要匹配事件源类型看具体需求。总之从接口的设计上是区分了事件类型以及事件源类型的判断。

实现SmartApplicationListener接口的Listener

我们以ConfigFileApplicationListener为代表进行观察。在AbstractApplicationEventMulticaster#supportsEvent函数里,因为它不是GenericApplicationListener的实现类,所以需要进行适配。
它的supportsEventType和supportsSourceType方法如下:

@Override
	public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
    
    
		return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType)
				|| ApplicationPreparedEvent.class.isAssignableFrom(eventType);
	}

	@Override
	public boolean supportsSourceType(Class<?> aClass) {
    
    
		return true;
	}

supportsEventType返回值取决于事件是否为ApplicationEnvironmentPreparedEvent和ApplicationPreparedEvent及其子类,而supportsSourceType直接返回true。
对以上4个具体的事件监听器的分析,我们可以得到如下结论:

直接实现的接口 依赖于泛型 可匹配的事件
ApplicationListener
GenericApplicationListener 多个
GenericApplicationListener和SmartApplicationListener 多个
SmartApplicationListener 多个

猜你喜欢

转载自blog.csdn.net/yyb_gz/article/details/108179699