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子类没重载这条方法。
在Abstra

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值