基于最新Spring 5.x,详细介绍了Spring的@EventListener事件发布机制的概念和使用方法,以及一些问题的解决办法!
事件发布机制,可以简单的理解为在系统达到某个状态或者进行某个操作的时候(这被称为事件源),导致某个事件被触发,对应的事件监听器会捕获到这个事件,明确系统达到了某个状态或者进行了某个操作,随后事件监听器进行一系列额外的操作。
比如发送短信或者邮件的操作,通常在一系列前置方法完成之后触发,那么发送短信或者邮件的方法可以置于一个事件监听器内部,然后当前一系列前置方法完成,发布一个事件,该监听器将会受到这个事件,这表示可以触发发送短信或者邮件的操作。
通常,事件发布机制用于代码解耦,异步事件还能提升响应速度,这类似于简单的消息队列,因此相比与真正的MQ,它的应用范围还是比较有限的,因为它的任务将会缓存到内存中,如果服务器重启或者挂了,那么没来得及执行的事件任务就没了。
Spring的ApplicationContext上下文容器中提供了一系列的事件发布机制,并通过ApplicationEvent类和ApplicationListener接口提供一系列默认的事件和监听器的实现。Spring 事件发布机制,本质上就是基于标准的观察者设计模式(Observer design pattern)。
Spring事件发布机制的关键类如下:
自 Spring 4.2 起,Spring事件发布机制得到显著增强,可以通过ApplicationEventPublisher发布任意自定义事件,并且事件不必再继承ApplicationEvent,它们自动包装为一个事件,并且还可以通过@EventListener注解监听事件。
Spring提供了一系列标准事件,也就是一系列容器状态相关的应用程序事件,它们都继承了ApplicationEvent。
Event | 描述 |
ContextRefreshedEvent | 上下文刷新事件。在ApplicationContext被初始化或者刷新完毕之后调用,即ConfigurableApplicationContext.refresh()方法尾部的publishEvent方法就会发布该类型事件。该事件发布之后,标志着容器初始化或者刷新完毕,所有的非延迟初始化的单例bean已被加载,后处理器bean都已回调完毕,上下文容易已准备好被使用。 |
ContextStartedEvent | 上下文启动事件。在ApplicationContext被启动之后调用,即ConfigurableApplicationContext.start()方法尾部的publishEvent方法就会发布该类型事件。该事件发布之后,标志着所有的Lifecycle包括SmartLifecycle已被启动(调用start()方法), |
ContextStoppedEvent | 上下文停止事件。在ApplicationContext被停止之后调用,即ConfigurableApplicationContext.stop()方法尾部的publishEvent方法就会发布该类型事件。该事件发布之后,标志着所有的Lifecycle包括SmartLifecycle已被停止(调用stop()方法)。被停止的上下文可通过start()重新启动。 |
ContextClosedEvent | 上下文关闭事件。在ApplicationContext被关闭之后调用,即ConfigurableApplicationContext.close()方法尾部的publishEvent方法就会发布该类型事件。该事件发布之后,所有单例bean将被销毁(并调用销毁回调),所有的Lifecycle将被停止(执行stop()方法)。容器销毁,无法刷新或重新启动。 |
RequestHandledEvent | 请求处理事件。仅仅适用与使用DispatcherServlet的web应用,该事件发布之后,标志着一次http请求完成。 |
ServletRequestHandledEvent | RequestHandledEvent的子类,用于添加特定于 Servlet 的上下文信息。 |
只需要很简单的配置即可使用Spring事件发布机制!
maven依赖:
<properties>
<spring-framework.version>5.2.8.RELEASEspring-framework.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>${spring-framework.version}version>
dependency>
dependencies>
一个自定义事件类:
/**
* 自定义事件
*
* @author lx
*/
public class MyApplicationEvent extends ApplicationEvent {
private String name;
/**
* @param source 事件发生或与之关联的对象(从不为null)
*/
public MyApplicationEvent(Object source, String name) {
super(source);
this.name = name;
}
public String getName() {
return name;
}
}
MyApplicationListener监听器类,监听MyApplicationEvent事件:
/**
* 自定义事件监听器,监听MyApplicationEvent事件
*
* @author lx
*/
@Component
public class MyApplicationListener implements ApplicationListener<MyApplicationEvent> {
/**
* 使用@Async开启异步事件
*/
@Async
@Override
public void onApplicationEvent(MyApplicationEvent event) {
System.out.println("-------------MyApplicationListener事件处理线程: " + Thread.currentThread().getName() + "-" + Thread.currentThread().hashCode());
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
System.out.println(event.getSource());
//创建事件的时间毫秒值
System.out.println(event.getTimestamp());
System.out.println(event.getName());
}
}
MyNewApplicationListener监听器类,监听PayloadApplicationEvent事件:
/**
* 自定义事件监听器,监听PayloadApplicationEvent事件
* PayloadApplicationEvent的泛型就是传递的参数的类型
*/
@Component
//注解排序
//@Order(1)
//@Priority(1)
public class MyNewApplicationListener implements ApplicationListener<PayloadApplicationEvent<String>>, PriorityOrdered {
/**
* 使用@Async开启异步事件
*/
@Async
@Override
public void onApplicationEvent(PayloadApplicationEvent<String> event) {
System.out.println("-------------MyNewApplicationListener事件处理线程: " + Thread.currentThread().getName() + "-" + Thread.currentThread().hashCode());
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
//传递的参数
System.out.println(event.getPayload());
System.out.println(event.getResolvableType());
System.out.println(event.getSource());
System.out.println(event.getTimestamp());
}
/**
* 实现了PriorityOrdered接口的优先级最高,其次才会比较order值
*/
@Override
public int getOrder() {
return 10;
}
}
AnnoApplicationListener监听器类,基于@EventListener注解:
/**
* Spring 4.2新的@EventListener注解
* 这里的AnnoApplicationListener没有实现ApplicationListener接口
*/
@Component
public class AnnoApplicationListener {
/**
* 使用@Async开启异步事件
*
* 这里表示监听PayloadApplicationEvent类型的事件
*/
@Async
@EventListener
//排序
@Order(5)
public void listen(PayloadApplicationEvent<String> event) {
System.out.println("-------------listen事件处理线程: " + Thread.currentThread().getName() + "-" + Thread.currentThread().hashCode());
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
//传递的参数
System.out.println(event.getPayload());
System.out.println(event.getResolvableType());
System.out.println(event.getSource());
System.out.println(event.getTimestamp());
}
/**
* 使用@Async开启异步事件
*
* 这里表示监听PayloadApplicationEvent类型的事件
*/
@Async
@EventListener
public void listen1(PayloadApplicationEvent<String> event) {
System.out.println("-------------listen1事件处理线程: " + Thread.currentThread().getName() + "-" + Thread.currentThread().hashCode());
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
//传递的参数
System.out.println(event.getPayload());
System.out.println(event.getResolvableType());
System.out.println(event.getSource());
System.out.println(event.getTimestamp());
}
/**
* 使用@Async开启异步事件
*
* 该方法具有String类型的参数,表示监听参数数据类型为String类型的事件
*/
@Async
@EventListener
public void listen2(String event) {
System.out.println("-------------listen2事件处理线程: " + Thread.currentThread().getName() + "-" + Thread.currentThread().hashCode());
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
//传递的参数
System.out.println(event);
}
/**
* 使用@Async开启异步事件
*
* 该方法没有参数,但是classes指定为String和Integer,表示监听参数数据类型为String和Integer类型的事件
*/
@Async
@EventListener(classes = {String.class, Integer.class})
public void listen3() {
System.out.println("-------------listen3事件处理线程: " + Thread.currentThread().getName() + "-" + Thread.currentThread().hashCode());
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
}
}
一个服务,内部获取了ApplicationEventPublisher,可以发布事件:
/**
* 一个服务,内部获取了ApplicationEventPublisher,可以发布事件
*
* @author lx
*/
@Component
public class EventService implements ApplicationEventPublisherAware {
/**
* 1 可以直接注入ApplicationEventPublisher
*/
@Resource
private ApplicationEventPublisher applicationEventPublisher1;
/**
* 2 自己接收ApplicationEventPublisher
*/
private ApplicationEventPublisher applicationEventPublisher2;
/**
* 实现ApplicationEventPublisherAware接口,将会自动回调setApplicationEventPublisher方法
*
* @param applicationEventPublisher Spring传递的applicationEventPublisher参数
*/
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
applicationEventPublisher2 = applicationEventPublisher;
}
/**
* 通用发布ApplicationEvent事件
*/
public void pushEvent(ApplicationEvent applicationEvent) {
applicationEventPublisher1.publishEvent(applicationEvent);
applicationEventPublisher2.publishEvent(applicationEvent);
}
/**
* Spring 4.2的新功能,发布任意事件,且不需要是ApplicationEvent类型
* 将会自动封装为一个PayloadApplicationEvent事件类型
*/
public void pushEvent(Object applicationEvent) {
applicationEventPublisher1.publishEvent(applicationEvent);
applicationEventPublisher2.publishEvent(applicationEvent);
}
/**
* 测试,通过这两个方式获取的applicationEventPublisher是否就是同一个并且就是当前上下文容器
*/
public void applicationEventPublisherTest() {
System.out.println(applicationEventPublisher1 instanceof AnnotationConfigApplicationContext);
System.out.println(applicationEventPublisher2 instanceof AnnotationConfigApplicationContext);
System.out.println(applicationEventPublisher1 == applicationEventPublisher2);
}
}
注解支持启动组件类:
@ComponentScan
@Configuration
//注解开启异步任务支持,没有这个注解无法开启异步任务
//@EnableAsync
public class StartConfig {
}
测试类:
public class EventTest {
private static EventService eventService;
static {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(StartConfig.class);
eventService = ac.getBean(EventService.class);
}
/**
* 测试获取的applicationEventPublisher是否就是同一个发布者,并且就是当前上下文容器
*/
public static void applicationEventPublisherTest() {
eventService.applicationEventPublisherTest();
}
/**
* 发布ApplicationEvent事件
*/
public static void pushEvent() {
//新建一个事件
MyApplicationEvent myApplicationEvent = new MyApplicationEvent("ApplicationEvent事件", "MyApplicationEvent");
//发布事件
System.out.println("---------发布事件-----------");
eventService.pushEvent(myApplicationEvent);
}
/**
* Spring 4.2的新功能,发布任意事件,且不需要是ApplicationEvent类型
* 将会自动封装为一个PayloadApplicationEvent事件类型
*/
public static void pushEventNew() {
//发布事件
System.out.println("---------Spring 4.2发布事件-----------");
eventService.pushEvent("PayloadApplicationEvent事件");
}
/**
* 如果事件类型不符合,那么不会被监听到
*/
public static void pushEventNotlisten() {
//发布事件
System.out.println("---------发布不会被监听到的事件-----------");
eventService.pushEvent(111111);
}
public static void main(String[] args) throws InterruptedException {
/*
* 1 测试获取的applicationEventPublisher是否就是同一个发布者,并且就是当前上下文容器
*/
applicationEventPublisherTest();
long start = System.currentTimeMillis();
System.out.println(start);
/*
* 2 发布ApplicationEvent事件
*/
pushEvent();
/*
* 3 Spring 4.2的新功能,发布任意事件,不需要是ApplicationEvent类型
* 将会自动封装为一个PayloadApplicationEvent事件类型
*/
pushEventNew();
/*
* 4 如果事件类型不符合,那么不会被监听到
*/
pushEventNotlisten();
/*
* 如果我们取消异步任务的支持,我们会发现,这些事件都是通过主线程同步执行的,到最后才会输出"事件发布返回"
* 而如果开启异步任务,那么事件的处理就不需要发布事件的线程执行了,提升了速度,主线程将很快返回
*/
System.out.println("---------事件发布返回,用时: " + (System.currentTimeMillis() - start));
}
}
相关文章:
https://spring.io/
Spring Framework 5.x 学习
Spring Framework 5.x 源码
如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!