陆续阅读了Spring Boot源码中,有不少地方都用到了监听器。每次看到监听器的代码都一头雾水,不懂其中的设计理念,这次决定肝一篇监听器的博文。
何为监听器?就是用来监听程序执行的。监听器可以做什么事?可以监听程序执行,使程序在不同的阶段做不同的事情,并且与程序主业务逻辑解耦。
那么读者读完是否很迷茫?如果学过MQ的可以把想象成一种订阅-发布模式,监听器首先订阅事件,然后其它功能主动发布事件,已触发监听器订阅的事件。
举个例子
:最常见的购物逻辑中,下订单以后就要扣库存,可以理解为扣库存这个操作监听了订单,这就是一种监听器,当下了订单,此时触发监听器,执行扣库存操作。有没有觉得监听器更像是订阅-发布模式
可能读完这个例子,大家觉得很难理解,这种下订单、扣库存,我在代码里面按照逻辑依次写下去不就完了,为啥要整的这么麻烦?
个人理解
:Spring提供的监听器,其实是一种设计模式,目的是为了更好的扩展性,例如像上面的下订单扣库存,肯定代码直接写最省事,但这样就耦合度高了,其它的业务功能都这样写嘛?如果Spring按照这种方式写,那么想扩展功能就只剩改源码了
所以从整体设计的角度,Spring框架为了提供更好的扩展性,采用了观察者模式,设计出了这么一套监听器,以供使用。这样开发人员不仅可以扩展Spring的监听器,也可以自定义新的监听器,一举两得。这样得以让代码更加优雅,便于扩展
Spring的监听器由以下3部分组成:
事件(ApplicationEvent)
:要广播,发送的消息. 监听器监听的事情监听器(ApplicationListener)
: 观察者模式中的观察者, 监听器监听特定事件, 并在内部定义了事件发生后的相应逻辑.事件发布器(ApplicationEventMulticaster)
:对应于观察者模式中的被观察者/主题.负责通知观察者. 对外提供发布事件和增删事件监听器的接口.维护事件和事件监听器之间的关系.并在事件发生时负责通知事件监听器.可能看到这3个词觉得很难理解,那么换一种解释:
事件
:负责调用逻辑,对应订阅-发布中的订阅监听器
:负责执行逻辑,也就是你的自定义逻辑事件发布器
:负责根据事件类型去调用你的自定义逻辑,底层就是一个循环,根据你传过来的事件类型,遍历调用对应的监听器上面介绍到的例子:下单后减库存. 减库存就是一个事件, 这个事件需要一个事件播放器, 将事件播放出去. 然后另一端事件监听器, 接收到信息,进行处理。简单写一下
【第1步
:创建一个订单实体类】
@Data
public class Order {
private String id;
private String name;
}
【第2步
:创建一个事件
,继承ApplicationEvent
】
// 继承ApplicationEvent必须重写构造方法
public class OrderEvent extends ApplicationEvent {
// 一般在调用的时候会把this传进来,也就是当前类
public OrderEvent(Object source) {
super(source);
}
// 可以在this的基础上,继续增加所需的参数
public OrderEvent(Object source, Order order) {
super(source);
System.out.println("创建了事件-----");
}
}
【第3步
:创建监听器
,实现ApplicationListener
】
@Component
public class OrderEventListener implements ApplicationListener<OrderEvent> {
@Override
public void onApplicationEvent(OrderEvent event) {
// 参数event可以接收传过来的参数,一般就是上一步逻辑处理的结果,也可以不传
System.out.println("触发了事件----------");
}
}
【第4步
:启动类】
@Configuration
@ComponentScan
public class TestMain {
@Test
public void m1() {
// 1.创建IOC容器
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(TestMain.class);
// 2.创建订单
Order order = new Order();
order.setId("订单号:i4u1212412312");
order.setName("商品名称:T恤");
System.out.println("订单创建完毕---" + order);
// 3.发布事件, 这里会借助事件分发器去调用监听器
ctx.publishEvent(new OrderEvent(this, order));
System.out.println("结束了-------");
}
}
写完以后,仔细思考一下,就会发现其实就是A调用B,但是Spring把A叫做事件,B叫做监听器,然后调用由事件分发器执行,解耦合
上面简单介绍了监听器,那么监听器有哪些呢?两种类型:一种是内置监听器, 一种是自定义监听器
Spring定义了一个内置监听器的父类ApplicationContextEvent
,实现该类的就是内置监听器
一共有4类实现了ApplicationContextEvent
Event | 说明 |
---|---|
ContextRefreshEvent |
当容器被实例化或者refresh时发布.如调用refresh()方法. 此处的实例化是指所有的bean都已被加载,后置处理器都被激活,所有单例bean都已被实例化,所有的容器对象都已经准备好可使用. 如果容器支持热重载,则refresh()可以被触发多次(XmlWebApplicationContext支持热刷新, 而GenericApplicationContext不支持热刷新) |
ContextStartedEvent |
当容器启动时发布, 即调用start()方法, 已启用意味着所有的lifecycle都已显示收到了start的信号 |
ContextStoppedEvent |
当容器停止时发布. 即调用stop()方法, 既所有的lifecycle bean都已显示接收了stop信号, 关闭的容器可以通过start()方法重启 |
ContextClosedEvent |
当容器关闭时发布. 即调用close()方法, 关闭意味着所有的单例bean都已被销毁. 关闭的容器不能被重启或refresh() |
总结
:这些内置监听器事件是Spring框架自己使用的,以供扩展Spring源码可以使用,例如:我想在Spring初始化完成以后执行某个操作,可以自定义监听器类,监听ContextRefreshEvent
事件,然后执行自定义逻辑
在简单点说,这些是Spring框架初始化默认发布的事件,无法更改事件,但你可以监听执行自定义逻辑,当然也可以自定义事件
【举个例子
】可以直接创建一个自定义监听器,监听ContextRefreshedEvent
事件,这样当Spring容器初始化完成,就会执行这个监听器的逻辑
@Component
public class ContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent> {
private String name = "qqweqwe123";
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("所有的bean初始化完成了");
}
}
自定义监听器,其实上面的例子已经写过1遍了,那么这里介绍一下实现的2种方式:
【方式1
:基于接口】
@Component
public class HelloEventListener implements ApplicationListener<OrderEvent> {
@Override
public void onApplicationEvent(OrderEvent event) {
if (event.getName().equals("减库存")) {
System.out.println("减库存....");
}
}
}
【方式2
:基于注解】
@Component
public class OrderEventListener {
@EventListener(OrderEvent.class)
public void onApplicationEvent(OrderEvent event) {
if (event.getName().equals("减库存")) {
System.out.println("减库存....");
}
}
}
监听器的初始化,都是在refresh()
中,那么下面就来看一下,跟监听器有关的类:
初始化一个监听器多播器,默认是SimpleApplicationEventMulticaster
,它负责将事件分发到监听器
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() + "]");
}
}
}
注册监听器,来源分为3步:
META-INF/spring.factories
中配置的protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// Publish early application events now that we finally have a multicaster...
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
protected void finishRefresh() {
// Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches();
// Initialize lifecycle processor for this context.
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// Publish the final event.
// 发布一个事件
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
经过以上3步,首先事件发布器有了,默认SimpleApplicationEventMulticaster
,监听器有了,已经注入进去了,剩下的就是通过事件发布器
,来触发监听器
初始化完毕以后,接下来就是使用了,当发布事件的时候,如何执行呢?
先说原理
:通过publishEvent()
发布事件以后,底层就会根据事件类型,循环判断所有的监听器,找到符合类型的,然后执行。
事件发布,然后交由时间发布器
执行
// AbstractApplicationContext.java
@Override
public void publishEvent(ApplicationEvent event) {
publishEvent(event, null);
}
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);
}
}
}
获取到匹配的监听器,然后循环执行
// SimpleApplicationEventMulticaster.java
@Override
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);
}
}
}
从全部的监听器中,遍历循环,找到支持该事件的监听器,这里代码只展示了大概,具体没有展开,大体就是一个for循环+if判断
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
Object source = event.getSource();
Class<?> sourceType = (source != null ? source.getClass() : null);
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
// Quick check for existing entry on ConcurrentHashMap...
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
if (this.beanClassLoader == null ||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
// Fully synchronized building and caching of a ListenerRetriever
synchronized (this.retrievalMutex) {
retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
retriever = new ListenerRetriever(true);
// 【这里会循环所有的监听器,根据 supportsEventType() 来判断是否支持该事件类型】
Collection<ApplicationListener<?>> listeners =
retrieveApplicationListeners(eventType, sourceType, retriever);
this.retrieverCache.put(cacheKey, retriever);
return listeners;
}
}
else {
// No ListenerRetriever caching -> no synchronization necessary
return retrieveApplicationListeners(eventType, sourceType, null);
}
}
接下来就是执行监听器了,看到invoke就知道了,反射调用对应的方法即可
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
// 【执行】
doInvokeListener(listener, event);
}
}
接下来就是执行监听器了,看到invoke就知道了,反射调用对应的方法即可
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
// 【执行】
doInvokeListener(listener, event);
}
}