1.前提知识点
(1)事件监听机制的好处是什么?
事件监听机制实际就是主题-订阅模式(观察者模式)的实现,能够降低代码耦合、简单的实现时间通知处理机制(有点像任务状态机)。
(2)SpringBoot事件监听核心接口和流程
在spring中,事件监听机制主要涉及4个实现类。
ApplicationEvent:事件,每个实现类表示一类事件,可携带数据。抽象类。
ApplicationListener:事件监听器,用于接收事件处理时间。接口。
ApplicationEventMulticaster:事件管理者,可以注册(添加)/移除/发布事件。用于事件监听器的注册和事件的广播。接口。
ApplicationEventPublisher:事件发布者,委托事件管理者ApplicationEventMulticaster完成事件发布。类。
2.SpringBoot自定义事件、监听器总结
为什么要先理解自定义事件监听实现?
只有理解自定义事件监听实现,体会到其中的设计思想,在看SpringBoot源码时才有助于理解SpringBoot内部事件监听器机制。
自定义事件、监听器流程:
流程总结:首先,自定义事件继承ApplicationEvent抽象类,可以定义一些方法。其次,自定义监听器实现ApplicationListener接口的onApplicationEvent方法/使用@EventListener注解。最后,调用context或注入ApplicationEventPublisher对象发布事件。
SpringBoot事件监听有四种方式,前三者都是实现ApplicationListener接口的onApplicationEvent方法,最后一个是注解实现自己的方法:
- 向ApplicationContext中添加监听器
- 将监听器作为组件加载到spring容器
- 在application.properties中配置监听器
- 通过@EventListener注解实现事件监听
2.1 自定义事件
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
2.2 自定义监听器
(1)向ApplicationContext中添加监听器
示例代码。下述代码前者实现了ApplicationListener接口onApplicationEvent方法的自定义监听器,后者添加监听器到context中。
//实现ApplicationListener接口的onApplicationEvent方法
public class MyEventListener1 implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent myEvent) {
Object msg = myEvent.getSource();
System.out.println("自定义事件监听器(MyEventListener1)收到发布的消息: " + msg);
}
}
//启动类
@SpringBootApplication
public class LearningApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(LearningApplication.class, args);
context.addApplicationListener(new MyEventListener1());//通过context添加监听器
}
}
(2)将监听器作为组件加载到spring容器
示例代码。将实现ApplicationListener接口的onApplicationEvent方法的自定义类,作为组件(@Componen)添加到springboot容器中。
@Component
public class MyEventListener2 implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent myEvent) {
Object msg = myEvent.getSource();
System.out.println("自定义事件监听器(MyEventListener2)收到发布的消息: " + msg);
}
}
(3)在application.properties中配置监听器
示例代码。自定义实现ApplicationListener接口的onApplicationEvent方法的自定义类,在配置文件中配置自定义监听器。
public class MyEventListener3 implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent myEvent) {
Object msg = myEvent.getSource();
System.out.println("自定义事件监听器(MyEventListener3)收到发布的消息: " + msg);
}
}
//application.properties文件中配置
context:
listener:
classes: com.xqnode.learning.common.event.MyEventListener3
(4)通过@EventListener注解实现事件监听
@Component
public class MyEventListener4 {
@EventListener//在方法上添加注解,表示该方法是监听器响应的方法
public void listener(MyEvent myEvent) {
Object msg = myEvent.getSource();
System.out.println("自定义事件监听器(MyEventListener4)收到发布的消息: " + msg);
}
}
2.3 发布自定义事件
只有发布了事件,监听器采集监听到事件,进而触发监听器方法中对应的操作。
(1)使用context发布事件
@SpringBootApplication
public class LearningApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(LearningApplication.class, args);
context.publishEvent(new MyEvent("测试事件"));
}
}
(2)注入ApplicationEventPublisher对象发布事件
@Autowired
private ApplicationEventPublisher applicationEventPublisher;//注入spring事件发布者,用于发布事件
applicationEventPublisher.publishEvent(new MyEvent("测试事件"));//发布事件。
3.SpringBoot自定义事件、监听器具体实现示例
整体流程:
自定义事件 ----> 利用ApplicationEventPublisher发布事件 ----> 使用注解@EventListener自定监听器 ----> 监听器内作相应处理
注:代码示例来源
(1)定义简单实体类
@Component
public class Order {
private String orderNo;
private String phone;
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
(2)自定义事件
@Component
public class AfterCreateOrderEvent extends ApplicationEvent {//继承ApplicationEvent
public AfterCreateOrderEvent(Order order) {
super(order);
}
public Order getOrder(){
return (Order) getSource();
}
}
(3)事件发布和自定义监听器
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;//注入spring事件发布者,用于发布事件
/**
* 简单的创建订单方法
*/
public void createOrder(Order order) {
System.out.println("创建订单 order:" + order.getOrderNo() + " 结束");
// TODO 1. 调用事件发布者发布事件,解耦
applicationEventPublisher.publishEvent(new AfterCreateOrderEvent(order));//发布事件。AfterCreateOrderEvent继承自ApplicationListener
System.out.println("createOrder方法 结束");
}
// TODO 2. 事件监听处理,加入@EventListener注解后,该方法可以看作事件监听者。参数就是自定义的事件。
@EventListener
public void afterCreateOrder(AfterCreateOrderEvent afterCreateOrderEvent) throws InterruptedException {
Order order = afterCreateOrderEvent.getOrder();
Thread.sleep(2000);
System.out.println("调用短信通知服务:" + order.getPhone());
System.out.println("调用积分服务增加贷款积分:" + order.getOrderNo());
}
}
(4)程序启动和结果
@RunWith(SpringRunner.class)
@SpringBootTest//引入了SpringBootTest测试用的jar包依赖
public class ApplicationTest {
@Autowired
private OrderService orderService;
@Test
public void eventTest() {
Order order = new Order();
order.setOrderNo("N123124124124");
order.setPhone("18782202534");
orderService.createOrder(order);
}
}
创建订单 order:N123124124124 结束
调用短信通知服务:18782202534
调用积分服务增加贷款积分:N123124124124
createOrder方法 结束
4.SpringBoot自定义监听器同步、异步分析
总结:通过applicationEventPublisher和@EventListener发布、接收事件,其实是同步的。这意味着,先后发布事件A、B,对应的监听器执行完毕A通知的事件之后,才会执行B通知的事件。
4.1 同步监听器代码示例:
验证逻辑:主线程同时发布2个事件,查看主线程是否会被阻塞。
/**
事件类
*/
public class MyEvent extends ApplicationEvent {
String threadName = "";
public MyEvent(Object source,String threadName){
super(source);
this.threadName = threadName;
}
public String getThreadName() {
return threadName;
}
public void setThreadName(String threadName) {
this.threadName = threadName;
}
}
/**
事件监听器,内部睡眠20s
*/
@Component
public class EventListenerHandler {
// @Async
@EventListener
public void listener(MyEvent myEvent){
System.out.println("监听器开始执行,当前线程名字:"+myEvent.getThreadName());
try {
Thread.sleep(20000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
/**
发布事件
*/
@Component
public class TestListenerRunner implements ApplicationRunner {
@Autowired
ApplicationEventPublisher applicationEventPublisher;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("runner开始...准备睡眠2s");
applicationEventPublisher.publishEvent(new MyEvent(new Object(),"thread1"));
applicationEventPublisher.publishEvent(new MyEvent(new Object(),"thread2"));
System.out.println("主线程执行结束...");
}
}
输出:
runner开始...准备睡眠2s
监听器开始执行,当前线程名字:thread1
可以看出,监听器内部代码逻辑被sleep阻塞,主线程等待监听器内部代码执行完毕才继续执行。
4.2 异步事件监听器代码示例
代码逻辑:在主应用类添加@EnableAsync注解表示线程池,同时在监听器方法处加入异步监听器。其他保持一致
@SpringBootApplication
@EnableAsync
public class SpringbootTestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootTestApplication.class, args);
}
}
@Component
public class EventListenerHandler {
@Async//异步监听器
@EventListener
public void listener(MyEvent myEvent){
System.out.println("监听器开始执行,当前线程名字:"+myEvent.getThreadName());
try {
Thread.sleep(20000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
输出:
runner开始...准备睡眠2s
主线程执行结束...
监听器开始执行,当前线程名字:thread1
监听器开始执行,当前线程名字:thread2
结果分析:由结果可以看出,主线程已经执行结束,两次发布的事件对应的监听事件都已经开始执行。
注意:异步监听器执行的顺序,和发布的顺序并不一致。
同时发布多个事件后,异步监听器打印结果如下:
runner开始...准备睡眠2s
主线程执行结束...
监听器开始执行,当前线程名字:thread1
监听器开始执行,当前线程名字:thread5
监听器开始执行,当前线程名字:thread6
监听器开始执行,当前线程名字:thread2
监听器开始执行,当前线程名字:thread7
监听器开始执行,当前线程名字:thread8
监听器开始执行,当前线程名字:thread3
监听器开始执行,当前线程名字:thread4