Spring的事件发布(ApplicationEventPublisher)和事件订阅(ApplicationListener)

Spring 的事件发布工具

平时开发中会发现Spring提供了一系列的工具满足我们的业务场景,其中有一项是提供了事件的发布和订阅。
事件的发布主要是依靠ApplicationEventPublisher来进行的。

要想实现事件发布主要需要下面几个对象:任务、事件、事件监听。

任务
任务就是一个普通的类,用来保存你要发布事件的内容。这个没有特殊限制,可以根据自己业务随意设置。

import lombok.Data;

/**
 *
 * @author daify
 * @date 2019-08-19
 **/
@Data
public class Task {

    private Long id;

    private String taskName;

    private String taskContext;

    private boolean finish;
}

事件
事件类需要继承org.springframework.context.ApplicationEvent,这样发布的事件才能被Spring所识别

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEvent;

/**
 *
 * @author daify
 * @date 2019-08-19
 **/
@Slf4j
public class MyEvent extends ApplicationEvent {
    
    private Task task;
    
    public MyEvent(Task task) {
        super(task);
        this.task = task;
    }

    public Task getTask() {
        return task;
    }
}

事件监听

事件的监听器需要实现org.springframework.context.ApplicationListener,并且需要注入到容器之中。

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import java.util.Objects;

/**
 *
 * @author daify
 * @date 2019-08-19
 **/
@Component
@Slf4j
public class MyEventListener implements ApplicationListener<MyEvent> {
    
    @Override 
    public void onApplicationEvent(MyEvent myEvent) {
        if (Objects.isNull(myEvent)) {
            return;
        }
        Task task = myEvent.getTask();
        log.info("事件接收任务:{}",JSON.toJSONString(task));
        task.setFinish(true);
        log.info("此时完成任务");
    }
}

测试

这样就可以通过下面测试内容完成事件发布的测试

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 *
 * @author daify
 * @date 2019-08-19
 **/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = 此处改为项目启动类.class)
@Slf4j
public class EventTest {
    
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    @Test
    public void publishTest() {
        Task task = new Task();
        task.setId(1L);
        task.setTaskName("测试任务");
        task.setTaskContext("任务内容");
        task.setFinish(false);
        MyEvent event = new MyEvent(task);
        log.info("开始发布任务");
        eventPublisher.publishEvent(event);
        log.info("发布任务完成");
    }
}

Spring 事件发布流程

Spring的事件发布和订阅还是相当简单的,这里就简单的描述下,整个事件发布的流程。

ApplicationEventPublisher

使用其publishEvent方法发布任务后,代码进入了
org.springframework.context.support.AbstractApplicationContext逻辑内。

AbstractApplicationContext

整个事件发布逻辑都在这个类以及其子类中,其最终发布事件的方法是publishEvent(Object event, @Nullable ResolvableType eventType)

	protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
		Assert.notNull(event, "Event must not be null");
		// 无关内容
		if (logger.isTraceEnabled()) {
			logger.trace("Publishing event in " + getDisplayName() + ": " + event);
		}

		// 类型转换
		ApplicationEvent applicationEvent;
		if (event instanceof ApplicationEvent) {
			applicationEvent = (ApplicationEvent) event;
		}
		else {
			applicationEvent = new PayloadApplicationEvent<>(this, event);
			if (eventType == null) {
				eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
			}
		}

		// 在早期事件,容器初始化时候使用,可以忽略
		if (this.earlyApplicationEvents != null) {
			this.earlyApplicationEvents.add(applicationEvent);
		}
		else {
		    // ② 进行任务广播的主要逻辑
			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
		}

		// 方便使用父类进行发布事件,非重点
		if (this.parent != null) {
			if (this.parent instanceof AbstractApplicationContext) {
				((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
			}
			else {
				this.parent.publishEvent(event);
			}
		}
	}

整个事件发布的核心逻辑在getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);这一行。
内容被分为两部分:

  1. getApplicationEventMulticaster()
    获得容器中的ApplicationEventMulticaster。此内容主要Spring用来辅助发布任务的工具类。

  2. ApplicationEventMulticaster.multicastEvent(applicationEvent, eventType)
    真正进行事件发布的内容。

ApplicationEventMulticaster

其为org.springframework.context.event.AbstractApplicationEventMulticaster实现类,主要为了辅助事件进行发布,
其内部发布任务主要核心逻辑在multicastEvent中。

	@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			Executor executor = getTaskExecutor();
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				invokeListener(listener, event);
			}
		}
	}

其大概流程为:

  1. 获取事件类型,主要用来获得Spring Event的实际类型。resolveDefaultEventType(event))
  2. getApplicationListeners(event, type)根据事件和事件类型去获得此事件和事件类型的监听器
  3. 使用invokeListener(listener, event)去执行事件。
AbstractApplicationEventMulticaster

获取监听器的主要逻辑在org.springframework.context.event.AbstractApplicationEventMulticaster中的getApplicationListeners(event, type)

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

大致流程:
通过时间类型和事件中的数据源类型,构建一个缓存key,先去缓存中获取有无此key对应的事件处理器。
如果不存在则构建一个新的ListenerRetriever,然后调用retrieveApplicationListeners方法获得监听的listener。

retrieveApplicationListeners

	private Collection<ApplicationListener<?>> retrieveApplicationListeners(
			ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) {

		List<ApplicationListener<?>> allListeners = new ArrayList<>();
		Set<ApplicationListener<?>> listeners;
		Set<String> listenerBeans;
		synchronized (this.retrievalMutex) {
			listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
			listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
		}
		for (ApplicationListener<?> listener : listeners) {
			if (supportsEvent(listener, eventType, sourceType)) {
				if (retriever != null) {
					retriever.applicationListeners.add(listener);
				}
				allListeners.add(listener);
			}
		}
		if (!listenerBeans.isEmpty()) {
			BeanFactory beanFactory = getBeanFactory();
			for (String listenerBeanName : listenerBeans) {
				try {
					Class<?> listenerType = beanFactory.getType(listenerBeanName);
					if (listenerType == null || supportsEvent(listenerType, eventType)) {
						ApplicationListener<?> listener =
								beanFactory.getBean(listenerBeanName, ApplicationListener.class);
						if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
							if (retriever != null) {
								if (beanFactory.isSingleton(listenerBeanName)) {
									retriever.applicationListeners.add(listener);
								}
								else {
									retriever.applicationListenerBeans.add(listenerBeanName);
								}
							}
							allListeners.add(listener);
						}
					}
				}
				catch (NoSuchBeanDefinitionException ex) {
					// Singleton listener instance (without backing bean definition) disappeared -
					// probably in the middle of the destruction phase
				}
			}
		}
		AnnotationAwareOrderComparator.sort(allListeners);
		if (retriever != null && retriever.applicationListenerBeans.isEmpty()) {
			retriever.applicationListeners.clear();
			retriever.applicationListeners.addAll(allListeners);
		}
		return allListeners;
	}

在这个方法中主要进行的逻辑就很简单了。主要通过循环Listeners来进行监听匹配。而Listeners的来源主要为两部分:

			listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
			listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);

一部分为容器中已经存在的监听器,一部分是监听器bean的字符串标识的名称。
applicationListenerBeans主要为了处理那些声明后还是还没有被添加进监听器集合中的bean。

事件发布的流程

获取事件发布工具
发布事件
获取事件监听发布工具
拼装事件监听缓存key
从缓存中取出监听器
存在
不存在,开始从容器中匹配监听器
匹配容器中的监听器
匹配容器中字符串表示的Bean
执行监听器方法
执行监听器方法
执行监听器方法
事件发布
ApplicationEventPublisher.publishEvent
AbstractApplicationContext.getApplicationEventMulticaster
SimpleApplicationEventMulticaster.multicastEvent
AbstractApplicationEventMulticaster.getApplicationListeners
ListenerCacheKey
retrieverCache
返回结果
retrieveApplicationListeners
applicationListeners
applicationListenerBeans
返回监听器结果
invokeListener
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大·风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值