文章目录
- 一、Java中事件/监听器编程模型
- 1、Java中Observable/Observer事件监听
- (1)代码实例
- (2)Java9新的事件监听
- 2、面向接口的事件/监听器设计模式
- 3、面向注解的事件/监听器设计模式
- 二、Spring事件
- 1、Spring标准事件-ApplicationEvent
- 2、基于接口的 Spring 事件监听器
- 代码实例
- 3、基于注解的 Spring 事件监听器
- 代码实例
- 4、注册Spring ApplicationListener
- 方法一:ApplicationListener 作为 Spring Bean 注册
- 方法二:通过 ConfigurableApplicationContext API 注册
- 方法三:直接使用注解@EventListener
- 三种方式的执行顺序
- 5、Spring 事件发布器
- (1)源码分析
- (2)依赖注入获取ApplicationEventPublisher
- (3)依赖查找 ApplicationEventMulticaster
- (4)ApplicationEventPublisher 发布事件
- (5)ApplicationEventPublisher与ApplicationEventMulticaster的关联
- 6、Spring 层次性上下文事件传播
- 代码实例
- 源码分析
- 事件重复处理
- 7、Spring 内建事件
- 源码分析
- 8、Spring 4.2 Payload 事件
- 代码实例及分析
- 9、自定义 Spring 事件
- 10、同步和异步 Spring 事件广播
- (1)基于实现类 - org.springframework.context.event.SimpleApplicationEventMulticaster
- (2)基于注解 - @org.springframework.context.event.EventListener
- (3)小tips
- 11、Spring 4.1 事件异常处理
- 源码分析
- 代码实例
- 12、Spring 事件/监听器实现原理
- 源码分析
- 基于注解的监听器如何注册
- 三、扩展-Spring Boot 、Spring Cloud事件
- 1、Spring Boot 事件
- 2、Spring Cloud 事件
- 参考资料
一、Java中事件/监听器编程模型
1、Java中Observable/Observer事件监听
设计模式 - 观察者模式扩展
• 可观者对象(消息发送者) - java.util.Observable
• 观察者 - java.util.Observer
标准化接口
• 事件对象 - java.util.EventObject
• 事件监听器 - java.util.EventListener
Observable/Observer 是观察者模型在 JDK 中的实现,而 EventObject 和 EventListener 是事件驱动的接口,这里有涉及实现,实现可以利用 Observable/Observer 或者扩展来完成。
Observable/Observer 是jdk中观察者模式的实现标准,有具体实现。但是事件驱动只是建议基于EventObject/EventListener来拓展,并没有具体实现。Spring事件并没有用到Observable/Observer ,并且Observable/Observer 在 jdk 9 被推荐转为java.util.concurrent.Flow实现。
JDK 中的 Observable/Observer 只是一个参考,它的执行顺序和插入顺序是相反,也即是它是 Stack 的方式,无法实现自定义顺序,并且它使用了 Vector 线程安全的集合容器,无法提升高性能,所以 Spring 没有必要用到它。
(1)代码实例
import java.util.EventListener;
import java.util.EventObject;
import java.util.Observable;
import java.util.Observer;
/**
* Java 事件监听示例
* {@link Observer} 示例
*/
public class ObserverDemo {
public static void main(String[] args) {
EventObservable observable = new EventObservable();
// 添加观察者(监听者)
observable.addObserver(new EventObserver());
// 发布消息(事件)
observable.notifyObservers("Hello,World");
}
static class EventObservable extends Observable {
public void setChanged() {
super.setChanged();
}
public void notifyObservers(Object arg) {
setChanged(); // 需要手动打开开关
super.notifyObservers(new EventObject(arg));
clearChanged();
}
}
static class EventObserver implements Observer, EventListener {
@Override
public void update(Observable o, Object event) {
EventObject eventObject = (EventObject) event;
System.out.println("收到事件 :" + eventObject);
}
}
}
(2)Java9新的事件监听
Observer和Observable在JDK9以后被废弃,因为观察者和可观察对象支持的事件模型是非常有限的,可观察对象传递的通知的顺序无法指定,状态变化与通知不能一一对应。
为了在线程之间可靠而有序地传递消息,可以考虑在java.util.concurrent包中使用一种并发数据结构。对于响应式流风格的编程,javaDOC提示考虑使用java.util.concurrent.Flow
2、面向接口的事件/监听器设计模式
事件/监听器场景举例:
Java 技术规范 | 事件接口 | 监听器接口 |
JavaBeans | java.beans.PropertyChangeEvent | java.beans.PropertyChangeListener |
Java AWT | java.awt.event.MouseEvent | java.awt.event.MouseListener |
Java Swing | javax.swing.event.MenuEvent | javax.swing.event.MenuListener |
Java Preference | java.util.prefs.PreferenceChangeEvent | java.util.prefs.PreferenceChangeListener |
通过上面列举的类的源码我们可以看到,Event都是继承了EventObject;Listener都是继承了EventListener,这基本是一个约定俗成的事情,Spring也不例外。
3、面向注解的事件/监听器设计模式
事件/监听器注解场景举例:
Java 技术规范 | 事件注解 | 监听器注解 |
Servlet 3.0+ | @javax.servlet.annotation.WebListener | |
JPA 1.0+ | @javax.persistence.PostPersist | |
Java Common | @PostConstruct | |
EJB 3.0+ | @javax.ejb.PrePassivate | |
JSF 2.0+ | @javax.faces.event.ListenerFor |
二、Spring事件
1、Spring标准事件-ApplicationEvent
上面我们提到过,Java 标准事件 java.util.EventObject 扩展的扩展特性就是事件发生事件戳。
而Spring 应用上下文 ApplicationEvent 扩展就是 ApplicationContextEvent,Spring 应用上下文(ApplicationContext)作为事件源。
具体实现:
• org.springframework.context.event.ContextClosedEvent
• org.springframework.context.event.ContextRefreshedEvent
• org.springframework.context.event.ContextStartedEvent
• org.springframework.context.event.ContextStoppedEvent
分析源码我们可以看出,ApplicationEvent抽象类继承了jdk的EventObject,ApplicationContextEvent抽象类又继承了ApplicationEvent。
ApplicationContextEvent就是Spring应用上下文的一个事件源,提供了获取ApplicationContext方法,事件的source就是ApplicationContext。
ApplicationContextEvent有以上四个实现:ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent。
2、基于接口的 Spring 事件监听器
Java 标准事件监听器 java.util.EventListener 扩展
• 扩展接口 - org.springframework.context.ApplicationListener
• 设计特点:单一类型事件处理
• 处理方法:onApplicationEvent(ApplicationEvent)
• 事件类型:org.springframework.context.ApplicationEvent
代码实例
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.support.GenericApplicationContext;
GenericApplicationContext context = new GenericApplicationContext();
// 向 Spring 应用上下文注册事件
context.addApplicationListener(new ApplicationListener<ApplicationEvent>() {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println(("ApplicationListener - 接收到 Spring 事件:" + event));
}
});
// 启动 Spring 应用上下文
context.refresh(); // ContextRefreshedEvent
// 启动 Spring 上下文(通常,ConfigurableApplicationContext#start() 方法是为了发布 ContextStartedEvent 事件,而真正的启动方法是 refresh() 方法。)
context.start(); // ContextStartedEvent
// 停止 Spring 上下文
context.stop(); // ContextStoppedEvent
// 关闭 Spring 应用上下文
context.close(); // ContextClosedEvent
我们通过该实例可以看出,Spring的四个事件通过Spring应用上下文的几个方法来触发的。
3、基于注解的 Spring 事件监听器
Spring 注解 - @org.springframework.context.event.EventListener
特性 | 说明 |
设计特点 | 支持多 ApplicationEvent 类型,无需接口约束 |
注解目标 | 方法 |
是否支持异步执行 | 支持 |
是否支持泛型类型事件 | 支持 |
是指支持顺序控制 | 支持,配合 @Order 注解控制 |
代码实例
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.event.*;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
@EnableAsync // 激活异步
public class MyApplicationListenerDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 将引导类 MyApplicationListenerDemo 作为 Configuration Class
context.register(MyApplicationListenerDemo.class);
// 启动 Spring 应用上下文
context.refresh(); // ContextRefreshedEvent
// 启动 Spring 上下文
context.start(); // ContextStartedEvent
// 停止 Spring 上下文
context.stop(); // ContextStoppedEvent
// 关闭 Spring 应用上下文
context.close(); // ContextClosedEvent
}
@EventListener
@Async // 需要@EnableAsync
public void onApplicationEventAsync(ContextRefreshedEvent event) {
println("@EventListener(异步) - 接收到 Spring ContextRefreshedEvent");
}
@EventListener
@Order(2)
public void onApplicationEvent(ContextStartedEvent event) {
println("@EventListener - 接收到 Spring ContextStartedEvent1");
}
@EventListener
@Order(1) // 可以使用@Order控制顺序,数字越小越先执行
public void onApplicationEvent2(ContextStartedEvent event) {
println("@EventListener - 接收到 Spring ContextStartedEvent2");
}
@EventListener
public void onApplicationEvent(ContextClosedEvent event) {
println("@EventListener - 接收到 Spring ContextClosedEvent");
}
@EventListener
public void onApplicationEvent(ContextStoppedEvent event) {
println("@EventListener - 接收到 Spring ContextStoppedEvent");
}
private static void println(Object printable) {
System.out.printf("[线程:%s] : %s\n", Thread.currentThread().getName(), printable);
}
}
执行结果:
[线程:SimpleAsyncTaskExecutor-1] : @EventListener(异步) - 接收到 Spring ContextRefreshedEvent
[线程:main] : @EventListener - 接收到 Spring ContextStartedEvent2
[线程:main] : @EventListener - 接收到 Spring ContextStartedEvent1
[线程:main] : @EventListener - 接收到 Spring ContextStoppedEvent
[线程:main] : @EventListener - 接收到 Spring ContextClosedEvent
使用注解监听Spring事件相对而言比较简单,而且可以手动实现异步、进行排序。
4、注册Spring ApplicationListener
方法一:ApplicationListener 作为 Spring Bean 注册
// 基于 ApplicationListener 注册为 Spring Bean
// 通过 Configuration Class 来注册
context.register(MyApplicationListener.class);
public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
println("MyApplicationListener - 接收到 Spring 事件:" + event);
}
}
方法二:通过 ConfigurableApplicationContext API 注册
以上通过接口、注解的方式其实就是使用方法二的方式进行注册的
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 将引导类 ApplicationListenerDemo 作为 Configuration Class
context.register(ApplicationListenerDemo.class);
// 基于 Spring 接口:向 Spring 应用上下文注册事件
// 基于 ConfigurableApplicationContext API 实现
context.addApplicationListener(new ApplicationListener<ApplicationEvent>() {
@Override
public void onApplicationEvent(ApplicationEvent event) {
println("ApplicationListener - 接收到 Spring 事件:" + event);
}
});
方法三:直接使用注解@EventListener
上面介绍过了
三种方式的执行顺序
通过运行代码我们发现,基于注解的Listener先执行,而后是基于接口的,最后才是注册为Bean的。
后面我们会通过分析源码来分析这个执行顺序的原因。
5、Spring 事件发布器
方法一:通过 ApplicationEventPublisher 发布 Spring 事件(事件发布)
• 获取 ApplicationEventPublisher:依赖注入
方法二:通过 ApplicationEventMulticaster 发布 Spring 事件(事件广播)
• 获取 ApplicationEventMulticaster:依赖注入、依赖查找
(1)源码分析
ApplicationEventPublisher接口有两个方法,其中有个方法是Spring4.2引入的。
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
void publishEvent(Object event);
}
ApplicationEventMulticaster接口会在ApplicationEventPublisher接口的基础上增加很多东西,包括对ApplicationListener的增删改查,multicastEvent方法就是事件广播方法。
public interface ApplicationEventMulticaster {
void addApplicationListener(ApplicationListener<?> listener);
void addApplicationListenerBean(String listenerBeanName);
void removeApplicationListener(ApplicationListener<?> listener);
void removeApplicationListenerBean(String listenerBeanName);
void removeAllListeners();
void multicastEvent(ApplicationEvent event);
void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}
(2)依赖注入获取ApplicationEventPublisher
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import javax.annotation.PostConstruct;
/**
* 注入 {@link ApplicationEventPublisher} 示例
*/
public class InjectingApplicationEventPublisherDemo implements ApplicationEventPublisherAware,
ApplicationContextAware, BeanPostProcessor {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@Autowired
private ApplicationContext applicationContext;
@PostConstruct
public void init() {
//# 3
applicationEventPublisher.publishEvent(new MySpringEvent("The event from @Autowired ApplicationEventPublisher"));
// #4
applicationContext.publishEvent(new MySpringEvent("The event from @Autowired ApplicationContext"));
}
public static void main(String[] args) {
// 创建注解驱动 Spring 应用上下文
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 注册 Configuration Class
context.register(InjectingApplicationEventPublisherDemo.class);
// 增加 Spring 事件监听器
context.addApplicationListener(new MySpringEventListener());
// 启动 Spring 应用上下文
context.refresh();
// 关闭 Spring 应用上下文
context.close();
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { // #1
applicationEventPublisher.publishEvent(new MySpringEvent("The event from ApplicationEventPublisherAware"));
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // #2
// ApplicationContext
applicationEventPublisher.publishEvent(new MySpringEvent("The event from ApplicationContextAware"));
}
}
通过以上实例我们可以看出,获取ApplicationEventPublisher 有两种方式,一种是通过 ApplicationEventPublisherAware 回调接口;一种是通过 @Autowired ApplicationEventPublisher。
而ApplicationContext也是继承了ApplicationEventPublisher,所以我们认为它也是一种ApplicationEventPublisher,也具有事件发布的能力。
(3)依赖查找 ApplicationEventMulticaster
查找条件
• Bean 名称:“applicationEventMulticaster”
• Bean 类型:org.springframework.context.event.ApplicationEventMulticaster
我们进行一下源码分析:
事实上,我们调用context.addApplicationListener()方法时,会委派给applicationEventMulticaster调用它的addApplicationListener方法。
// org.springframework.context.support.AbstractApplicationContext#addApplicationListener
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
Assert.notNull(listener, "ApplicationListener must not be null");
if (this.applicationEventMulticaster != null) {
this.applicationEventMulticaster.addApplicationListener(listener);
}
this.applicationListeners.add(listener);
}
而此处的applicationEventMulticaster初始化方法:
// org.springframework.context.support.AbstractApplicationContext#initApplicationEventMulticaster
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) { // applicationEventMulticaster
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() + "]");
}
}
}
initApplicationEventMulticaster这个方法是在springIOC容器refresh时被调用的。
而实际上ApplicationEventPublisher 发布事件的底层实现,也是通过getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); 实现的。ApplicationEventPublisher 没有直接实现。
(4)ApplicationEventPublisher 发布事件
通过以下实例我们可以看出,实现了ApplicationEventPublisherAware 接口可以获取到ApplicationEventPublisher ,从而直接发送事件(这种事件貌似只能通过addApplicationListener添加的监听器接收到,通过注解的方式是接收不到的,具体原因尚不明确)。
import org.springframework.context.*;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyApplicationListenerDemo implements ApplicationEventPublisherAware {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 将引导类 MyApplicationListenerDemo 作为 Configuration Class
context.register(MyApplicationListenerDemo.class);
context.addApplicationListener(new ApplicationListener<ApplicationEvent>() {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println(("ApplicationListener - 接收到 Spring 事件:" + event));
}
});
// 启动 Spring 应用上下文
context.refresh(); // ContextRefreshedEvent
// 启动 Spring 上下文
context.start(); // ContextStartedEvent
// 停止 Spring 上下文
context.stop(); // ContextStoppedEvent
// 关闭 Spring 应用上下文
context.close(); // ContextClosedEvent
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
applicationEventPublisher.publishEvent(new ApplicationEvent("Hello,World"){});
// 发送 PayloadApplicationEvent
applicationEventPublisher.publishEvent("Hello,World2");
}
}
也可以通过通过 @Autowired ApplicationEventPublisher 来依赖注入ApplicationEventPublisher 。
(5)ApplicationEventPublisher与ApplicationEventMulticaster的关联
底层实现:
• 接口:org.springframework.context.event.ApplicationEventMulticaster
• 抽象类:org.springframework.context.event.AbstractApplicationEventMulticaster
• 实现类:org.springframework.context.event.SimpleApplicationEventMulticaster
实际上,ApplicationEventPublisher与ApplicationEventMulticaster之间是没有接口依赖关系的。
那么他们两个是怎么关联的呢?
我们通过context调用publishEvent方法,里面的逻辑我们再看一下:
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 {
// 关键逻辑!获取ApplicationEventMulticaster
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);
}
}
}
我们可以看到,ApplicationEventPublisher的publishEvent方法底层其实是调用了ApplicationEventMulticaster的multicastEvent方法,而SimpleApplicationEventMulticaster作为ApplicationEventMulticaster的一个默认实现,是支持异步事件发送的:
// org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
@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);
}
}
}
话又说回来,earlyApplicationEvents 是什么呢?
其实这是Spring4开始支持的,就是早期的Event。Spring3其实有个bug,如果一个类同时实现ApplicationEventPublisherAware、BeanPostProcessor或者BeanFactoryPostProcessor,就会出问题。
SpringIOC在初始化时执行的refresh方法:
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
我们发现,如果一个bean实现了BeanPostProcessor,会在initApplicationEventMulticaster初始化ApplicationEventMulticaster之前初始化,所以当此时发布事件的时候,ApplicationEventMulticaster还未初始化,导致报错,所以使用一个earlyApplicationEvents 临时存储,当registerListeners()方法执行完之后,此时ApplicationEventMulticaster已经完全初始化了,这时候取出所有存储的earlyApplicationEvents ,进行挨个事件发布。
所以,这相当于一个Spring的BUG,只不过在后期修复了。
6、Spring 层次性上下文事件传播
发生说明:
当 Spring 应用出现多层次 Spring 应用上下文(ApplicationContext)时,如 Spring WebMVC、Spring Boot或 Spring Cloud 场景下,由子 ApplicationContext 发起 Spring 事件可能会传递到其 Parent ApplicationContext(直到 Root)的过程。
如何避免:
定位 Spring 事件源(ApplicationContext)进行过滤处理。
代码实例
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.event.ApplicationContextEvent;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* 层次性 Spring 事件传播实例
*/
public class HierarchicalSpringEventPropagateDemo {
public static void main(String[] args) {
// 1. 创建 parent Spring 应用上下文
AnnotationConfigApplicationContext parentContext = new AnnotationConfigApplicationContext();
parentContext.setId("parent-context");
// 注册 MyListener 到 parent Spring 应用上下文
parentContext.register(MyListener.class);
// 2. 创建 current Spring 应用上下文
AnnotationConfigApplicationContext currentContext = new AnnotationConfigApplicationContext();
currentContext.setId("current-context");
// 3. current -> parent
currentContext.setParent(parentContext);
// 注册 MyListener 到 current Spring 应用上下文
currentContext.register(MyListener.class);
// 4.启动 parent Spring 应用上下文
parentContext.refresh();
// 5.启动 current Spring 应用上下文
currentContext.refresh();
// 关闭所有 Spring 应用上下文
currentContext.close();
parentContext.close();
}
static class MyListener implements ApplicationListener<ApplicationContextEvent> {
private
//static
Set<ApplicationContextEvent> processedEvents = new LinkedHashSet<>();
@Override
public void onApplicationEvent(ApplicationContextEvent event) {
if (processedEvents.add(event)) {
System.out.printf("监听到 Spring 应用上下文[ ID : %s ] 事件 :%s\n", event.getApplicationContext().getId(),
event.getClass().getSimpleName());
}
}
}
}
执行结果:
监听到 Spring 应用上下文[ ID : parent-context ] 事件 :ContextRefreshedEvent
监听到 Spring 应用上下文[ ID : current-context ] 事件 :ContextRefreshedEvent
监听到 Spring 应用上下文[ ID : current-context ] 事件 :ContextRefreshedEvent
监听到 Spring 应用上下文[ ID : current-context ] 事件 :ContextClosedEvent
监听到 Spring 应用上下文[ ID : current-context ] 事件 :ContextClosedEvent
监听到 Spring 应用上下文[ ID : parent-context ] 事件 :ContextClosedEvent
我们可以看出,每个事件会执行三次,因为current执行完毕之后,还会在其parent中执行一次。
源码分析
我们可以看一下context的refresh方法触发的finishRefresh方法中publishEvent触发的事件逻辑:
// org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)
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) { // 递归,如果有parent的话,就传递给parent进行事件发布
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
事件重复处理
比较简单的方式就是定义一个全局变量,如上我们的代码实例,定义了一个static的set做去重处理,同一个事件保证只能监听一次。
也可以注入ApplicationContext,判断事件的上下文对象是否是当前的上下文对象。从而进行过滤。
7、Spring 内建事件
ApplicationContextEvent 派生事件
• ContextRefreshedEvent :Spring 应用上下文就绪事件
• ContextStartedEvent :Spring 应用上下文启动事件
• ContextStoppedEvent :Spring 应用上下文停止事件
• ContextClosedEvent :Spring 应用上下文关闭事件
start 和 stop 方法是一种辅助的特性,通常使用不多。
源码分析
我们来分析一下context的refresh方法:
refresh方法最终会调用finishRefresh方法,会调用publishEvent方法发送ContextRefreshedEvent事件:
// org.springframework.context.support.AbstractApplicationContext#finishRefresh
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);
}
实际上,AbstractApplicationContext实现了ApplicationContext,ApplicationContext继承了ApplicationEventPublisher,所以在底层是可以发布事件的。
其他几个事件发送同理。
8、Spring 4.2 Payload 事件
Spring Payload 事件 - org.springframework.context.PayloadApplicationEvent
使用场景:简化 Spring 事件发送,关注事件源主体
发送方法:ApplicationEventPublisher#publishEvent(java.lang.Object)
代码实例及分析
import org.springframework.context.*;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyApplicationListenerDemo implements ApplicationEventPublisherAware {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 将引导类 MyApplicationListenerDemo 作为 Configuration Class
context.register(MyApplicationListenerDemo.class);
context.addApplicationListener(new ApplicationListener<ApplicationEvent>() {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println(("ApplicationListener - 接收到 Spring 事件:" + event));
}
});
// 启动 Spring 应用上下文
context.refresh(); // ContextRefreshedEvent
// 启动 Spring 上下文
context.start(); // ContextStartedEvent
// 停止 Spring 上下文
context.stop(); // ContextStoppedEvent
// 关闭 Spring 应用上下文
context.close(); // ContextClosedEvent
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
applicationEventPublisher.publishEvent(new ApplicationEvent("Hello,World"){});
// 发送 PayloadApplicationEvent
applicationEventPublisher.publishEvent("Hello,World2");
applicationEventPublisher.publishEvent(new HelloPayloadApplicationEvent(this, "Hello,World3"));
}
static class HelloPayloadApplicationEvent extends PayloadApplicationEvent<String> {
public HelloPayloadApplicationEvent(Object source, String payload) {
super(source, payload);
}
}
}
通过运行我们可以看出,使用下面这种方式发送PayloadApplicationEvent会报错:
applicationEventPublisher.publishEvent(new HelloPayloadApplicationEvent(this, "Hello,World3"));
错误提示:
Mismatched number of generics specified
当我们把HelloPayloadApplicationEvent类改一下:
static class HelloPayloadApplicationEvent<String> extends PayloadApplicationEvent<String> {
public HelloPayloadApplicationEvent(Object source, String payload) {
super(source, payload);
}
}
此时就不会报错了,正常发送我们的事件。
实际上,Spring一直没有处理过这个问题,在Payload事件中Bean的泛型具体化并没有处理好。
关于Spring泛型处理:Spring泛型处理源码详解,Java泛型处理
所以,想要发送PayloadApplicationEvent事件,只需要使用上面的那种方式即可,不需要再继承、new一个Event了:
// 发送 PayloadApplicationEvent
applicationEventPublisher.publishEvent("Hello,World2");// (√)
applicationEventPublisher.publishEvent(new HelloPayloadApplicationEvent(this, "Hello,World3")); // (×)
9、自定义 Spring 事件
(1)扩展 org.springframework.context.ApplicationEvent
import org.springframework.context.ApplicationEvent;
/**
* 自定义 Spring 事件
*/
public class MySpringEvent extends ApplicationEvent {
/**
* Create a new {@code ApplicationEvent}.
*
* @param message 事件消息
*/
public MySpringEvent(String message) {
super(message);
}
@Override
public String getSource() {
return (String) super.getSource();
}
public String getMessage() {
return getSource();
}
}
(2)实现 org.springframework.context.ApplicationListener
import org.springframework.context.ApplicationListener;
/**
* 自定义 Spring 事件监听器
*/
public class MySpringEventListener implements ApplicationListener<MySpringEvent> {
@Override
public void onApplicationEvent(MySpringEvent event) {
System.out.printf("[线程 : %s] 监听到事件 : %s\n", Thread.currentThread().getName(), event);
}
}
(3)注册 org.springframework.context.ApplicationListener
GenericApplicationContext context = new GenericApplicationContext();
// 1.添加自定义 Spring 事件监听器
context.addApplicationListener(new MySpringEventListener());
// 2.启动 Spring 应用上下文
context.refresh();
// 3. 发布自定义 Spring 事件
context.publishEvent(new MySpringEvent("Hello,World"));
// 4. 关闭 Spring 应用上下文
context.close();
10、同步和异步 Spring 事件广播
(1)基于实现类 - org.springframework.context.event.SimpleApplicationEventMulticaster
模式切换:setTaskExecutor(java.util.concurrent.Executor) 方法
• 默认模式:同步
• 异步模式:如 java.util.concurrent.ThreadPoolExecutor
设计缺陷:非基于接口契约编程
源码分析:
ApplicationEventMulticaster的一个默认实现就是SimpleApplicationEventMulticaster,它的multicastEvent如下:
// org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
@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);
}
}
}
如果手动设置(setTaskExecutor)了taskExecutor,就是异步执行。
代码实例:
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 异步事件处理器示例
*/
public class AsyncEventHandlerDemo {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
// 1.添加自定义 Spring 事件监听器
context.addApplicationListener(new MySpringEventListener());
// 2.启动 Spring 应用上下文
context.refresh(); // 初始化 ApplicationEventMulticaster
// 依赖查找 ApplicationEventMulticaster
ApplicationEventMulticaster applicationEventMulticaster =
context.getBean(AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
// 判断当前 ApplicationEventMulticaster 是否为 SimpleApplicationEventMulticaster
if (applicationEventMulticaster instanceof SimpleApplicationEventMulticaster) {
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster =
(SimpleApplicationEventMulticaster) applicationEventMulticaster;
// 切换 taskExecutor
ExecutorService taskExecutor = Executors.newSingleThreadExecutor(new CustomizableThreadFactory("my-spring-event-thread-pool"));
// 同步 -> 异步
simpleApplicationEventMulticaster.setTaskExecutor(taskExecutor);
// 添加 ContextClosedEvent 事件处理,优雅关闭线程池
applicationEventMulticaster.addApplicationListener(new ApplicationListener<ContextClosedEvent>() {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
if (!taskExecutor.isShutdown()) {
taskExecutor.shutdown();
}
}
});
}
// 3. 发布自定义 Spring 事件
context.publishEvent(new MySpringEvent("Hello,World"));
// 4. 关闭 Spring 应用上下文(ContextClosedEvent)
context.close();
}
}
import org.springframework.context.ApplicationListener;
/**
* 自定义 Spring 事件监听器
*/
public class MySpringEventListener implements ApplicationListener<MySpringEvent> {
@Override
public void onApplicationEvent(MySpringEvent event) {
System.out.printf("[线程 : %s] 监听到事件 : %s\n", Thread.currentThread().getName(), event);
}
}
(2)基于注解 - @org.springframework.context.event.EventListener
模式切换
• 默认模式:同步
• 异步模式:标注 @org.springframework.scheduling.annotation.Async
实现限制:无法直接实现同步/异步动态切换
事实上注解方式实现异步,上面我们已经介绍过了。
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import static java.util.concurrent.Executors.newSingleThreadExecutor;
/**
* 注解驱动异步事件处理器示例
*/
@EnableAsync // 激活 Spring 异步特性
public class AnnotatedAsyncEventHandlerDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 1. 注册当前类作为 Configuration Class
context.register(AnnotatedAsyncEventHandlerDemo.class);
// 2.启动 Spring 应用上下文
context.refresh(); // 初始化 ApplicationEventMulticaster
// 3. 发布自定义 Spring 事件
context.publishEvent(new MySpringEvent("Hello,World"));
// 4. 关闭 Spring 应用上下文(ContextClosedEvent)
context.close();
}
@Async // 同步 -> 异步
@EventListener
public void onEvent(MySpringEvent event) {
System.out.printf("[线程 : %s] onEvent方法监听到事件 : %s\n", Thread.currentThread().getName(), event);
}
@Bean
public Executor taskExecutor() {
ExecutorService taskExecutor = newSingleThreadExecutor(new CustomizableThreadFactory("my-spring-event-thread-pool-a"));
return taskExecutor;
}
}
(3)小tips
上面的例子我们可以看出,基于实现类SimpleApplicationEventMulticaster手动设置线程池的方式来实现异步,其实是全局设置。
而使用注解的方式,可以指定方法异步。
11、Spring 4.1 事件异常处理
Spring 3.0 错误处理接口 - org.springframework.util.ErrorHandler
使用场景:
• Spring 事件(Events):SimpleApplicationEventMulticaster Spring 4.1 开始支持
• Spring 本地调度(Scheduling):org.springframework.scheduling.concurrent.ConcurrentTaskScheduler和org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
源码分析
上面我们分析到,SimpleApplicationEventMulticaster发布事件执行multicastEvent方法,最终会调用invokeListener来调用监听器:
// org.springframework.context.event.SimpleApplicationEventMulticaster#invokeListener
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) { // 如果有ErrorHandler
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else { // 没有ErrorHandler 就直接执行
doInvokeListener(listener, event);
}
}
代码实例
public class AsyncEventHandlerDemo {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
// 启动 Spring 应用上下文
context.refresh(); // 初始化 ApplicationEventMulticaster
// 依赖查找 ApplicationEventMulticaster
ApplicationEventMulticaster applicationEventMulticaster =
context.getBean(AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
// 判断当前 ApplicationEventMulticaster 是否为 SimpleApplicationEventMulticaster
if (applicationEventMulticaster instanceof SimpleApplicationEventMulticaster) {
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster =
(SimpleApplicationEventMulticaster) applicationEventMulticaster;
simpleApplicationEventMulticaster.setErrorHandler(e -> {
System.err.println("当 Spring 事件异常时,原因:" + e.getMessage());
});
}
context.addApplicationListener(new ApplicationListener<MySpringEvent>() {
@Override
public void onApplicationEvent(MySpringEvent event) {
System.out.printf("[线程 : %s] 监听到事件 : %s\n", Thread.currentThread().getName(), event);
throw new RuntimeException("故意抛出异常");
}
});
// 3. 发布自定义 Spring 事件
context.publishEvent(new MySpringEvent("Hello,World"));
// 4. 关闭 Spring 应用上下文(ContextClosedEvent)
context.close();
}
}
12、Spring 事件/监听器实现原理
通过上面我们的分析与实践,大致基本也了解了Spring 事件/监听器实现原理,在此我们进一步做一下总结。
核心类 - org.springframework.context.event.SimpleApplicationEventMulticaster
设计模式:观察者模式扩展
• 观察者 - org.springframework.context.ApplicationListener:API 添加、依赖查找
• 通知对象 - org.springframework.context.ApplicationEvent
执行模式:同步/异步
异常处理:org.springframework.util.ErrorHandler
泛型处理:org.springframework.core.ResolvableType
源码分析
SimpleApplicationEventMulticaster先继承了AbstractApplicationEventMulticaster,
AbstractApplicationEventMulticaster又实现了ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware。
在addApplicationListener时,实际上发生了以下事情:
// org.springframework.context.event.AbstractApplicationEventMulticaster#addApplicationListener
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
synchronized (this.retrievalMutex) {
// Explicitly remove target for a proxy, if registered already,
// in order to avoid double invocations of the same listener.
Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
if (singletonTarget instanceof ApplicationListener) {
this.defaultRetriever.applicationListeners.remove(singletonTarget);
}
this.defaultRetriever.applicationListeners.add(listener);
this.retrieverCache.clear();
}
}
defaultRetriever就是ListenerRetriever一个内部类,保存着ApplicationListener的集合:
public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
在AbstractApplicationEventMulticaster定义了一个
final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);
ListenerCacheKey就是将ApplicationEvent的泛型,里面存着ResolvableType。
实际上,我们上面分析到,调用multicastEvent方法时,会获取所有的ApplicationListener:
// org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
@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);
}
}
}
// org.springframework.context.event.AbstractApplicationEventMulticaster#getApplicationListeners(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
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);
}
}
在获取所有的Listener时,会首先通过泛型构造一个ListenerCacheKey,来确定查找监听指定类型的监听器,迭代这些监听器挨个进行通知。
这也是可以解释了,为什么可以单独监听指定事件的类型了:
context.addApplicationListener(new ApplicationListener<ApplicationEvent>() {
@Override
public void onApplicationEvent(ApplicationEvent event) {
println("ApplicationListener - 接收到 Spring 事件:" + event);
}
});
context.addApplicationListener(new ApplicationListener<ContextRefreshedEvent>() {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
println("ApplicationListener - 接收到 Spring 事件:" + event);
}
});
基于注解的监听器如何注册
1.org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry) 注册所有相关注解的post processor到beanRegistry里面,其中就包括监 听器相关的EventListenerMethodProcessor
2.该类的这个方法org.springframework.context.event.EventListenerMethodProcessor#afterSingletonsInstantiated会在bean实例化的中被调用。
3.org.springframework.context.event.EventListenerMethodProcessor#processBean 最终在这个方法找到相关注解并不listener注册到ApplicationContext中
其实spring的很多功能都是通过扩展post processor来完成的。
三、扩展-Spring Boot 、Spring Cloud事件
1、Spring Boot 事件
事件类型 | 发生时机 |
ApplicationStartingEvent | 当 Spring Boot 应用已启动时 |
ApplicationStartedEvent | 当 Spring Boot 应用已启动时 |
ApplicationEnvironmentPreparedEvent | 当 Spring Boot Environment 实例已准备时 |
ApplicationPreparedEvent | 当 Spring Boot 应用预备时 |
ApplicationReadyEvent | 当 Spring Boot 应用完全可用时 |
ApplicationFailedEvent | 当 Spring Boot 应用启动失败时 |
实际上,Spring Boot事件基本上还是使用Spring事件那一套-ApplicationEvent。
也是用的ApplicationEventPublisher来发布的事件。
关于SpringBoot事件详细用法请移步:
Springboot启动之后立即执行某些方法可以怎么做?Springboot监听器,Springboot生命周期钩子函数总结大全
2、Spring Cloud 事件
事件类型 | 发生时机 |
EnvironmentChangeEvent | 当 Environment 示例配置属性发生变化时 |
HeartbeatEvent | 当 DiscoveryClient 客户端发送心跳时 |
InstancePreRegisteredEvent | 当服务实例注册前 |
InstanceRegisteredEvent | 当服务实例注册后 |
RefreshEvent | 当 RefreshEndpoint 被调用时 |
RefreshScopeRefreshedEvent | 当 Refresh Scope Bean 刷新后 |
Spring Cloud 事件和Spring事件也都差不多,Spring Cloud 事件会分到不同的功能模块里面。
参考资料
极客时间-《小马哥讲 Spring 核心编程思想》