ApplicationContext中的事件处理是通过ApplicationEvent类和ApplicationListener接口提供的。如果将实现ApplicationListener接口的bean部署到上下文中,则每次将ApplicationEvent发布到ApplicationContext时,都会通知该bean。从本质上讲,这是标准的观察者设计模式。从Spring 4.2开始,提供了一个基于注释的事件模型,以及发布任何任意事件(即不一定从ApplicationEvent扩展的对象)的能力。
事件机制
Spring的事件机制是为同一应用程序容器中Spring bean之间的简单通信而设计的。机制工作示意图:
工作过程如下:
1、定义事件
2、事件发布bean通过发布器发布消息(默认发布器是)ApplicationContext。
3、发布器调用广播机广播方法。
4、广播机选择匹配消息的事件接收Bean,调用onApplication处理消息。
事件接收Bean在初始化过程中自动注册到广播机。
事件发布
发布实现
发布事件的bean通过调用ApplicationEventPublisher.publishEvent(Object event)发布事件,该方法实现在AbstractApplicationContext中,代码如下:
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 applEvent) {
applicationEvent = applEvent;
}
else {
//创建为一个PayloadApplicationEvent
applicationEvent = new PayloadApplicationEvent<>(this, event, eventType);
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 {
//立即广播 注1
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
//如果存在父容器,在父容器中广播
// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext abstractApplicationContext) {
abstractApplicationContext.publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
注1:事件广播器实际就是对所有Listener进行注册管理,在广播时逐一按事件匹配广播。事件广播器如果由应用实现,需实现ApplicationEventMulticaster接口;如果应用没有实现,则用spring提供的SimpleApplicationEventMulticaster作为事件广播器。
框架事件
ContextRefreshedEvent:ApplicationContext 初始化或刷新时发布。对应实现AbstractApplicationContext.finishRefresh()
ContextStartedEvent:ApplicationContext 调用start()方法时发布。对应实现AbstractApplicationContext.start()
ContextStoppedEvent:ApplicationContext 调用stop()方法时发布。对应实现AbstractApplicationContext.stop()
ContextClosedEvent:ApplicationContext 调用close()方法时发布,或者JVM关闭时发布。对应实现AbstractApplicationContext.doClose()
RequestHandledEvent:在WebApplicationContext中,当一个request完成时发布。
ServletRequestHandledEvent:RequestHandledEvent 子类,在ServletContext下使用。
自定义事件
定义事件
自定义事件继承ApplicationEvent 。
public class BlockedEmailEvent extends ApplicationEvent {
//事件类的属性可以当做事件传参
private final String email;
private final String content;
public BlockedEmailEvent(Object source, String email, String content) {
//ApplicationEvent构建
super(source);
this.email= email;
this.content = content;
}
}
事件发布
若要发布自定义ApplicationEvent,在ApplicationEventPublisher上调用publishEvent()方法。通常,这是通过创建一个实现ApplicationEventPublisherware的类并将其注册为Springbean来完成的。下面实例就是发送email时,如果是黑名单,则不发送并发送BlockedEmailEvent
@Component
public class EmailService implements ApplicationEventPublisherAware {
//阻止发送名单
private List<String> blockedList;
//发布器
private ApplicationEventPublisher publisher;
public void setBlockedList(List<String> blockedList) {
this.blockedList = blockedList;
}
//使用容器的事件发布器(容器会自动调用该方法)
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void sendEmail(String email, String content) {
//通知没有发送的email
if (blockedList.contains(email)) {
publisher.publishEvent(new BlockedEmailEvent(this, email, content));
return;
}
// 真正发送email...
}
}
事件接收
事件接收分为同步接收和异步接收。
同步接收
ApplicationListener同步接收事件,表示发送者的publishEvent()方法会阻塞,直到所有侦听器都完成了对事件的处理。同步一个优点是,当侦听器接收到事件时,如果事务上下文可用,它将在发布者的事务上下文中操作。
代码方式
public class BlockedEmailListener implements ApplicationListener<BlockedEmailEvent> {
public void onApplicationEvent(BlockedEmailEvent event) {
//接收到对应事件,进行处理
// notify appropriate parties via notificationAddress...
}
}
注解方式
public class BlockedEmailListener {
//注解,参数为事件
@EventListener
public void processBlockedEmailEvent(BlockedEmailEvent event) {
// notify appropriate parties via notificationAddress...
}
}
异步接收
public class BlockedEmailListener {
//注解,参数为事件
@EventListener
@Async
public void processBlockedEmailEvent(BlockedEmailEvent event) {
// notify appropriate parties via notificationAddress...
}
}
处理顺序
如果一个事件有多个Listener,且有先后顺序要求,用注解@Order,通常同@EventListener配套使用。
public class BlockedEmailListener {
//注解,参数为事件
@EventListener
@Order(5)
public void processBlockedEmailEvent(BlockedEmailEvent event) {
// notify appropriate parties via notificationAddress...
}
}