1. 使用Spring 事件
首先spring事件分为事件发布者(EventPublisher)、事件监听者(EventListener),还包括一个事件广播者(这个是spring实现相关,这一节不讨论)。使用spring事件机制,需要自定义事件发布者和监听者。
以一个简单的例子开始,事件发布者发送一个string类型的消息,接受者将接收到的消息打印出来。
- 事件发布者
@Component
public class SaySomethingPublisher implements ApplicationEventPublisherAware{
private ApplicationEventPublisher applicationEventPublisher;
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
public void saySomething(String msg){
applicationEventPublisher.publishEvent(msg);
}
}
需要实现ApplicationEventPublisherAware这个Aware接口,广播事件需要利用applicationEventPublisher
关于ApplicationEventPublisherAware如下:
public interface ApplicationEventPublisher {
void publishEvent(ApplicationEvent event);
void publishEvent(Object event);
}
用户发布的事件类型可以是:
1. 用户可以继承ApplicationEvent从而自定义Event类型
2. 也可以使用任意Object类型,但是如果event真实类型不是ApplicationEvent的话,那么event会被封装成PayloadApplicationEvent
- 事件监听者
//事件监听者需要实现ApplicationListener接口
@Component
public class ListenerA implements ApplicationListener<PayloadApplicationEvent<String>> {
// 由于监听的是String类型的事件会被封装成PayloadApplicationEvent,所以此处类型是PayloadApplicationEvent
public void onApplicationEvent(PayloadApplicationEvent event) {
// getSource返回真实的事件
Object msg = event.getSource();
System.out.println("ListenerA receive:" + msg);
}
}
关于发布出去的事件,那些监听者会监听到?
- 发布的事件类型是ApplicationEvent的实现类A
那么所有监听者的onApplicationEvent
的参数类型是A或者A的子类都会收到事件。 - 发布的事件类型是不是ApplicationEvent类型,类型是B
这种情况下,最终事件会被包装成PayloadApplicationEvent<B>
, 那么所有监听者方法onApplicationEvent
的参数是PayloadApplicationEvent<B>
的监听者会收到, 假设有C是B的父类,且有一个监听者X监听PayloadApplicationEvent<C>
,那X是收不到PayloadApplicationEvent<B>
类型的事件的
2. Spring事件原理
Spring事件机制是观察者模式的一种实现,但是除了发布者和监听者者两个角色之外,还有一个EventMultiCaster的角色负责把事件转发给监听者,工作流程如下:
也就是说上面代码中发布者调用applicationEventPublisher.publishEvent(msg);
是会将事件发送给了EventMultiCaster, 而后由EventMultiCaster注册着所有的Listener,然后根据事件类型决定转发给那个Listener。
2.1 EventMultiCaster
ApplicationContext完成bean的装配和初始化后(非lazy-init的singleton bean会加载后就初始化),会尝试创建一个eventMultiCaster,创建代码如下:
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
//判断有没有一个name是“applicationEventMulticaster”且实现了“ ApplicationEventMulticaster”的bean,有的话那它就是eventMultiCaster
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isDebugEnabled()) {
logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
// 没有这样一个bean,那就会创建一个默认的
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
"': using default [" + this.applicationEventMulticaster + "]");
}
}
}
- 默认SimpleApplicationEventMulticaster
直接看一下SimpleApplicationEventMulticaster用来广播event的代码:
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
// 这个是用来根据event的类型找到合适的listener的
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
// executor不是空的时候会在executor中激活listener
if (executor != null) {
executor.execute(new Runnable() {
@Override
public void run() {
invokeListener(listener, event);
}
});
}
// 否则就直接在当前调用线程中激活listener
else {
invokeListener(listener, event);
}
}
}
在创建SimpleApplicationEventMulticaster时,executor是null,所以默认情况下所有的listener 的onApplicationEvent
是直接在当前线程(事件发布者所在线程)中调用,所以
如果onApplicationEvent有阻塞操作也会导致事件发布者被阻塞,后续的其他listener也会被阻塞无法调用。
- 自定义multicaster
「2.1」中介绍spring会加载一个叫applicationEventMulticaster
且实现了ApplicationEventMulticaster
接口的multicaster,自定义multicaster需要实现了该接口然后将bean的名字设为applicationEventMulticaster
即可。
下面的例子为默认的SimpleApplicationEventMulticaster
添加了executor,以使事件发布者和监听者不用在同一个线程中调用:
//使用线程池运行listener
<bean id="executorService" class="java.util.concurrent.Executors" factory-method="newCachedThreadPool">
</bean>
<bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">
<property name="taskExecutor" ref="executorService">
</property>
</bean>
3. 附
- Spring启动完成之后(已经完成bean解析,non-lazy-init的singleton实例化和初始化,完成listener的注册),默认会发布一个
ContextRefreshedEvent
事件,该事件包装的消息是一个ApplicationContext对象。