概述
spring引入了事件机制,支持应用事件 ApplicationEvent、应用监听器 ApplicationListener。
事件驱动的3要素
- 事件:具有事件源、发生时间2个属性
- 事件发布器:负责发布事件
- 事件监听器:监听指定类型的事件,
spring的事件驱动模型
观察者模式的典型应用,监听器订阅指定类型的事件,事件发布器发布事件时,会自动通知订阅了该事件类型的所有监听器。
源码分析
ApplicationEvent 应用事件
public abstract class ApplicationEvent extends EventObject {
private static final long serialVersionUID = 7099057708183571937L;
//事件的发生时间,long型时间戳
private final long timestamp;
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
public ApplicationEvent(Object source, Clock clock) {
super(source);
this.timestamp = clock.millis();
}
public final long getTimestamp() {
return this.timestamp;
}
}
jdk自带的对象事件 EventObject 定义了一个Object类型的成员变量表示事件源,ApplicationEvent 在 EventObject 的基础上增加了一个long型的成员变量记录事件的发生时间。
spring中常见的事件类型
ApplicationEvent 表示spring的应用事件,是应用事件的基类,实现类众多,对应具体的事件类型。
spring常见的事件类型(5种标准事件)
方法名称 |
触发时机 |
ContextStartedEvent 上下文开始事件 |
refresh()刷新过程中,调用高级容器的 start() 方法开启高级容器(的生命周期管理)时触发 |
ContextRefreshedEvent 上下文已刷新事件 |
refresh() 完成刷新时触发 |
ContextStoppedEvent 上下文已停止事件 |
容器关闭过程中,调用高级容器的 stop() 方法终止容器(的生命周期管理)时触发 |
ContextClosedEvent 上下文已关闭事件 |
容器关闭完成后触发 |
RequestHandledEvent 请求已处理事件 |
请求处理完成时触发 如果是 springmvc,会直接使用 子类事件 ServletRequestHandledEvent |
4+1,4个容器|上下文生命周期相关的,1个处理请求相关的。
spring支持自定义的应用事件,可以继承 ApplicationEvent 实现自定义的应用事件。
ApplicationListener 应用监听器
//继承了jdk自带的事件监听器 EventListener,泛型指定要监听的事件类型
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
//处理事件
void onApplicationEvent(E event);
static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {
return event -> consumer.accept(event.getPayload());
}
}
ApplicationEventPublisher 事件发布器
@FunctionalInterface
public interface ApplicationEventPublisher {
//发布事件,默认实现是调用下面的方法
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
//发布事件,参数是要发布的事件
void publishEvent(Object event);
}
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
//...
}
AbstractApplicationContext 实现了事件发布器,可以调用AbstractApplicationContext实例发布事件。
自定义事件
/**
* 自定义事件,需要继承 ApplicationEvent
*/
public class MyApplicationEvent extends ApplicationEvent {
/**
* 在构造方法中调用父类对应的构造方法
*
* @param source 指定事件源
*/
public MyApplicationEvent(Object source) {
super(source);
}
}
自定义应用监听器
方式一 ApplicationListener接口+放到容器中
//实现ApplicationListener接口,泛型指定要监听的事件类型,ApplicationEvent 则表示监听所有的应用事件
@Component //放到容器中
public class MyApplicationListener implements ApplicationListener<MyApplicationEvent> {
/**
* 处理事件
*
* @param event
*/
@Override
public void onApplicationEvent(MyApplicationEvent event) {
//...
}
}
一个类对应一个应用监听器,往往需要编写多个自定义的应用监听器类
方式二 @EventListener注解
@Component //标注为bean,这样才会解析其中的 @EventListener 注解
public class MyApplicationListener {
/**
* 使用 @EventListener 标注为应用监听器,形参类型即要监听的事件类型
*
* @param event 事件
*/
@EventListener
public void MyApplicationEvent(MyApplicationEvent event) {
//...
}
}
一个方法对应一个应用监听器,可在一个类中写多个应用监听器
方式三 ApplicationListener接口+代码配置
此种方式适合springboot应用。
自定义的应用监听器
//不需要、也不能放到容器中,否则会出现重复创建监听器实例、重复监听的情况
public class MyApplicationListener implements ApplicationListener<MyApplicationEvent> {
@Override
public void onApplicationEvent(MyApplicationEvent event) {
//...
}
}
修改main()方法如下
//创建spring容器
SpringApplication springApplication = new SpringApplication(DemoApplication.class);
//添加应用监听器
springApplication.addListeners(new MyApplicationListener());
//启动容器
springApplication.run(args);
必看的注意事项 | 使用建议
1、第一、二种方式
这2种方式都会把监听器放到容器中,在 refersh() 刷新上下文时会通过 beanFactory.getBean() 创建监听器实例放到容器中。监听器类|方法中可以使用 @Value、@Autowired、@Bean之类的spring注解。
如果应用监听器中用于处理事件的回调方法执行很耗时、逻辑上允许异步执行,又是springboot应用,那么可以在方法上标注 springboot自带的 @Async 注解,将方法标注为异步方法,让方法异步执行,不阻塞主线程。
引导类上需要加 @EnableAsync,@Async 才会生效。
2、第三种方式
通过spring的factories机制实现,以springboot应用为例,在上下文|容器创建之前就创建了应用监听器实例,绑定到运行监听器内置的事件广播器上,生效比较早,这种方式定义的监听器通常用于在启动阶段监听默认的运行监听器发布的事件。
是通过反射调用无参构造器创建实例,不需要、也不能使用@Component之类的注解放到容器中,否则会出现重复创建监听器实例、重复监听的情况。
因为是在上下文|容器创建之前就创建监听器实例,类上也不使用@Component之类的注解,所以不能在监听器类中使用 @Value、@Autowired、@Bean之类的spring注解,或者说这些注解无效。
3、使用建议
通常使用第一二种即可。
第三四种创建监听器实例的时机比较早,可以监听高级容器 refresh() 之前的事件,比如 默认的运行监听器发布的时间,如果在refresh()之前就要监听事件,可以使用第三种。
需要注意的是,第三种是通过反射调用无参构造器创建实例,第四种是直接new调用构造方法创建实例,创建的监听器实例都不会放到容器中,但高级容器refresh()后,这些监听器实例依然有效、起作用。
发布事件
可以通过高级容器 AbstractApplicationContext、或者事件广播器 ApplicationEventMulticaster 发布事件,这2个都会作为bean自动放到容器中,直接注入使用即可
@Autowired
private ApplicationContext applicationContext;
@Autowired
private ApplicationEventMulticaster applicationEventMulticaster;
public void publish() {
//事件,参数指定事件源
MyApplicationEvent myApplicationEvent = new MyApplicationEvent(this);
//通过 applicationEventMulticaster 发布事件
applicationEventMulticaster.multicastEvent(myApplicationEvent);
//通过 applicationContext 发布事件
applicationContext.publishEvent(myApplicationEvent);
}
作为事件广播器来说,ApplicationContext 是高级容器,包含了太多东西,偏重量级,ApplicationEventMulticaster 比较纯粹,更推荐用 ApplicationEventMulticaster。