spring中的事件监听机制

介绍

example

spring支持自定义的事件发布。

spring的事件监听机制基于ApplicationListener这个接口。

/*
Interface to be implemented by application event listeners.Based on the standard {@code java.util.EventListener} interface
for the Observer design pattern. As of Spring 3.0, an ApplicationListener can generically declare the event type that it is interested in. When registered with a Spring ApplicationContext, events will be filtered accordingly, with the listener getting invoked for matching event objects only.
*/

这个接口继承了 java.util.EventListener。基于观察者模式。

ApplicationListener 的实现类会在spring容器启动的时候被发现。


我们可以简单的看一下spring它自带的ApplicationListener

@Component
public class MyEventListener implements ApplicationListener {
	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		System.out.println("event source: " + event.getSource());
		System.out.println("event: " + event.toString());
	}
}

在这里我们打印事件源和ApplicationEvent本身。

启动容器:

public class SpringEventsTest {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext applicationContext = 
		new AnnotationConfigApplicationContext(SpringEventsConfig.class);
	}
}

控制台:

event source: org.springframework.context.annotation.
AnnotationConfigApplicationContext@23fc625e, 
started on Sat Jun 20 21:20:00 GMT+08:00 2020

event: org.springframework.context.event.
ContextRefreshedEvent[source=org.springframework.context.annotation.
AnnotationConfigApplicationContext@23fc625e, 
started on Sat Jun 20 21:20:00 GMT+08:00 2020


事件源就是AnnotationConfigApplicationContext,事件是ContextRefreshedEvent

其实我们还可以看到一个关闭事件,加上一句话:

AnnotationConfigApplicationContext ac = 
new AnnotationConfigApplicationContext(SpringEventsConfig.class);
ac.close();

这时候的日志就多了一个关闭事件:

event source: org.springframework.context.annotation.
AnnotationConfigApplicationContext@244038d0, 
started on Wed Jun 08 17:27:50 CST 2022

event: org.springframework.context.event.
ContextClosedEvent[source=org.springframework.context.annotation.
AnnotationConfigApplicationContext@244038d0, 
started on Wed Jun 08 17:27:50 CST 2022]

简单原理解释

我们可以稍稍看一下调用链,先打一个断点:

在这里插入图片描述

在这里插入图片描述
事件发布是refresh的最后一个方法。

在这里插入图片描述

将刷新事件传入publishEvent中。

在这里插入图片描述
他去获取了一个Multicaster(广播器)去广播事件。

在这里插入图片描述
但是我们似乎不知道这个applicationEventMulticaster是从什么时候赋值的(先假设有值了)。

在这里插入图片描述

在这里插入图片描述

然后他获取所有的监听器,依次调用他们的回调方法onApplicationEvent

现在又有问题了:

在这里插入图片描述

在我去获取监听器的时候就已经能够获取到了,那么这些监听器是何时加到spring容器中的呢?


我们首先看看那个广播器是如何加入到容器中的?

在这里插入图片描述

在这里插入图片描述

这个东西是new出来然后干到容器中的。

在这里插入图片描述
在这里插入图片描述

它通过getBeanNamesForType获取到了我们自定义的监听器,当然这不能说监听器放进了spring容器中,只能说广播器获取到了我们自定义的监听器。

自定义事件、监听和发布

事件
public class CustomSpringEvent extends ApplicationEvent {
	private static final long serialVersionUID = -8053143381029977953L;

	private String message;

	public CustomSpringEvent(final Object source, final String message) {
		super(source);
		this.message = message;
	}

	public String getMessage() {
		return message;
	}

}

自定义事件需要继承ApplicationEvent

监听器

有了事件之后,我们需要一个监听器来监听事件:

@Component
public class CustomSpringEventListener implements ApplicationListener<CustomSpringEvent> {

	@Override
	public void onApplicationEvent(final CustomSpringEvent event) {
		System.out.println("Received spring custom event - " + event.getMessage());
	}

}

自定义监听器要实现ApplicationListener接口。

onApplicationEvent方法最终会被spring回调,它的含义是消费事件。

发布者

一个事件必须要基于发布者才能够发布,如此才能被监听器监听到:

@Component
public class CustomSpringEventPublisher implements ApplicationEventPublisherAware {

	private ApplicationEventPublisher applicationEventPublisher;

	public void publishEvent(final String message) {
		System.out.println("Publishing custom event. ");
		final CustomSpringEvent customSpringEvent = new CustomSpringEvent(this, message);
		applicationEventPublisher.publishEvent(customSpringEvent);
	}

	@Override
	public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
		this.applicationEventPublisher = applicationEventPublisher;
	}
}

发布者要实现ApplicationEventPublisherAware接口。

我们将spring容器传进来的ApplicationEventPublisher保存起来,然后调用它的api发布事件。

测试

我们从容器中拿到发布者,然后发布事件。spring会自动监听到,然后消费事件:


public class SpringEventsTest {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringEventsConfig.class);
		CustomSpringEventPublisher customSpringEventPublisher = applicationContext.getBean("customSpringEventPublisher", CustomSpringEventPublisher.class);
		customSpringEventPublisher.publishEvent("lalala");
	}
}

结果:

Publishing custom event. 
Received spring custom event - lalala

更加一般的事件

实际上,一个事件并不用继承ApplicationEvent。我们可以写一个任意的事件:

public class GenericSpringEvent<T> {

	private final T what;
	protected final boolean success;

	public GenericSpringEvent(final T what, final boolean success) {
		this.what = what;
		this.success = success;
	}

	public T getWhat() {
		return what;
	}

	public boolean isSuccess() {
		return success;
	}

}

这个事件包含事件是什么以及是否被成功消费

为了表示可扩展性,我们写一个具体的String事件来继承它:

public class GenericStringSpringEvent extends GenericSpringEvent<String> {

	GenericStringSpringEvent(final String what, final boolean success) {
		super(what, success);
	}

}

这种情况的监听器比较特殊,我们必须用@EventListener注解来标注:

@Component
public class AnnotationDrivenEventListener {
	private boolean hitSuccessfulEventHandler = false;
	
	@EventListener(condition = "#event.success")
	public void handleSuccessful(final GenericSpringEvent<String> event) {
		System.out.println("Handling generic event (conditional): " + event.getWhat());
		hitSuccessfulEventHandler = true;
	}
}

这个注解上面我们还附加了条件,使用的是SpEl表达式。

只有conditiontrue,才能回调handleSuccessful方法。


至于事件的发布,与之前讲的自定义事件发布是一样的:

	public void publishGenericEvent(final String message, boolean success) {
		System.out.println("Publishing generic event.");
		final GenericSpringEvent<String> genericSpringEvent = new GenericStringSpringEvent(message, success);
		applicationEventPublisher.publishEvent(genericSpringEvent);
	}

测试:

public class SpringEventsTest {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringEventsConfig.class);
		CustomSpringEventPublisher customSpringEventPublisher = applicationContext.getBean("customSpringEventPublisher", CustomSpringEventPublisher.class);
		customSpringEventPublisher.publishGenericEvent("generic event",true);
	}
}

结果:

Publishing generic event.
Handling generic event (conditional): generic event

如果发布事件的时候将success改为false

customSpringEventPublisher.publishGenericEvent("generic event",false);

那么发布的事件将无法被监听到。

@EventListener原理

为什么一个bean加了@EventListener注解就能后监听到事件呢?换句话说,这个注解是由谁解析的,时机又是在什么时候。

在这里插入图片描述
我们需要关注一个类:EventListenerMethodProcessor

在这里插入图片描述
他是一个什么呢?他是一个SmartInitializingSingleton,这个东西就出现在bean的生命周期里了。

在这里插入图片描述

在完成bean的创建后,还要进行一步判断,看这个bean是不是SmartInitializingSingleton

在这里插入图片描述
此时我们的EventListenerMethodProcessor就会进来解析。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值