原文网址:SpringBoot--事件监听机制--使用/实例/原理_IT利刃出鞘的博客-CSDN博客
简介说明
本文用示例介绍SpringBoot中的事件的用法及原理。
SpringBoot的启动过程用到了事件监听机制,它属于观察者模式。学习事件监听机制可以让我们更好地理解SpringBoot的启动流程,也有利于将观察者模式应用于项目。
事件监听简述
事件的发布与监听从属于观察者模式;和MQ相比,事件的发布与监听偏向于处理“体系内”的某些逻辑。
多个监听器可以监听同一个事件。例如:发布了事件A,监听器A和监听器B都监听了事件A,则监听器A和B都会进行处理。
同步与异步监听
| 监听方式 | 特点 | 使用时机 |
| 同步监听 | 发布器线程与监听器线程处于同一线程 | 1.监听逻辑处理较快 2.需要紧接着根据监听器追踪业务线程 |
| 异步监听 | 发布器线程与监听器线程处于不同线程 | 1.监听逻辑处理比较耗时 2.追求响应性能 |
优先级
可使用实现Ordered接口的方式,调整监听器顺序。
注意:必须是同时实现 ApplicationListener
下边几种都是无法控制顺序的:
- @Component+@EventListerner+实现Ordered
- 实现 ApplicationListener
+@Order
同步监听(无序)
事件
package com.example.event;
import org.springframework.context.ApplicationEvent;
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
监听器
监听器1
package com.example.event;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyListener {
@EventListener
public void abc(MyEvent event) {
System.out.println("监听器: " + "MyListener");
System.out.println("监听器所在线程:" + Thread.currentThread().getName());
System.out.println("事件: " + event);
System.out.println("事件的数据: " + event.getSource());
}
}
监听器2
package com.example.event;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyListener2 {
@EventListener
public void onApplicationEvent(MyEvent event) {
System.out.println("监听器: " + "MyListener2");
System.out.println(" 所在线程: " + Thread.currentThread().getName());
System.out.println(" 事件: " + event);
System.out.println(" 事件的数据:" + event.getSource());
}
}
发布器
package com.example.event;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class MyPublisher {
@Autowired
ApplicationContext applicationContext;
public void myPublish(String message) {
System.out.println("发布器所在线程:" + Thread.currentThread().getName());
applicationContext.publishEvent(new MyEvent(message));
}
}
测试
写一个Controller
package com.example.controller;
import com.example.event.MyPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
MyPublisher myPublisher;
@GetMapping("/test1")
public String test1() {
myPublisher.myPublish("Hello");
return "test1 success";
}
}
启动后,访问:http://localhost:8080/test1
后端输出:
发布器所在线程:http-nio-8080-exec-1 监听器: MyListener 所在线程: http-nio-8080-exec-1 事件: com.example.event.MyEvent[source=Hello] 事件的数据:Hello 监听器: MyListener2 所在线程: http-nio-8080-exec-1 事件: com.example.event.MyEvent[source=Hello] 事件的数据:Hello
可以发现,所有监听器和发布器都在同一个线程。
同步监听(有序)事件
package com.example.event;
import org.springframework.context.ApplicationEvent;
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
监听器
监听器1
package com.example.event; import org.springframework.context.ApplicationListener; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; @Component public class MyListener implements ApplicationListener, Ordered { @Override public void onApplicationEvent(MyEvent event) { System.out.println("监听器: " + "MyListener"); System.out.println(" 所在线程: " + Thread.currentThread().getName()); System.out.println(" 事件: " + event); System.out.println(" 事件的数据:" + event.getSource()); } @Override public int getOrder() { return 2; } }
监听器2
package com.example.event; import org.springframework.context.ApplicationListener; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; @Component public class MyListener2 implements ApplicationListener, Ordered { public void onApplicationEvent(MyEvent event) { System.out.println("监听器: " + "MyListener2"); System.out.println(" 所在线程: " + Thread.currentThread().getName()); System.out.println(" 事件: " + event); System.out.println(" 事件的数据:" + event.getSource()); } @Override public int getOrder() { return 1; } }
发布器
package com.example.event;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class MyPublisher {
@Autowired
ApplicationContext applicationContext;
public void myPublish(String message) {
System.out.println("发布器所在线程:" + Thread.currentThread().getName());
applicationContext.publishEvent(new MyEvent(message));
}
}
测试
写一个Controller
package com.example.controller;
import com.example.event.MyPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
MyPublisher myPublisher;
@GetMapping("/test1")
public String test1() {
myPublisher.myPublish("Hello");
return "test1 success";
}
}
启动后,访问:http://localhost:8080/test1
后端输出:
发布器所在线程:http-nio-8080-exec-1 监听器: MyListener2 所在线程: http-nio-8080-exec-1 事件: com.example.event.MyEvent[source=Hello] 事件的数据:Hello 监听器: MyListener 所在线程: http-nio-8080-exec-1 事件: com.example.event.MyEvent[source=Hello] 事件的数据:Hello
如果将监听器的实现的Ordered顺序颠倒,则输出结果如下:
发布器所在线程:http-nio-8080-exec-1 监听器: MyListener 所在线程: http-nio-8080-exec-1 事件: com.example.event.MyEvent[source=Hello] 事件的数据:Hello 监听器: MyListener2 所在线程: http-nio-8080-exec-1 事件: com.example.event.MyEvent[source=Hello] 事件的数据:Hello异步监听(无序)
方法:
- 开启异步监听
- 在监听器上加@Async(此监听器必须是@Component方法注册的)
事件
package com.example.event;
import org.springframework.context.ApplicationEvent;
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
同步监听器
package com.example.event;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyListener {
@EventListener
public void abc(MyEvent event) {
System.out.println("监听器: " + "MyListener");
System.out.println(" 所在线程: " + Thread.currentThread().getName());
System.out.println(" 事件: " + event);
System.out.println(" 事件的数据:" + event.getSource());
}
}
异步监听器
package com.example.event;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
@Async
public class MyListener2 {
@EventListener
public void onApplicationEvent(MyEvent event) {
System.out.println("监听器: " + "MyListener2");
System.out.println(" 所在线程: " + Thread.currentThread().getName());
System.out.println(" 事件: " + event);
System.out.println(" 事件的数据:" + event.getSource());
}
}
发布器
package com.example.event;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class MyPublisher {
@Autowired
ApplicationContext applicationContext;
public void myPublish(String message) {
System.out.println("发布器所在线程:" + Thread.currentThread().getName());
applicationContext.publishEvent(new MyEvent(message));
}
}
启动类
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
测试
写一个Controller
package com.example.controller;
import com.example.event.MyPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
MyPublisher myPublisher;
@GetMapping("/test1")
public String test1() {
myPublisher.myPublish("Hello");
return "test1 success";
}
}
启动后,访问:http://localhost:8080/test1
后端输出:
发布器所在线程:http-nio-8080-exec-1 监听器: MyListener 所在线程: http-nio-8080-exec-1 事件: com.example.event.MyEvent[source=Hello] 事件的数据:Hello 监听器: MyListener2 所在线程: task-1 事件: com.example.event.MyEvent[source=Hello] 事件的数据:Hello流程 自定义事件
通过继承ApplicationEvent来自定义事件。
构造器的参数为该事件的相关数据对象,监听器可以获取到该数据对象,进而进行相关逻辑处理。
package com.example.event;
import org.springframework.context.ApplicationEvent;
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
自定义监听器
ApplicationListener
法1:@EventListener
监听单个事件
package com.example.event;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
public class MyListener {
@EventListener
public void abc(MyEvent event) {
System.out.println("监听器: " + "MyListener");
System.out.println(" 线程: " + Thread.currentThread().getName());
System.out.println(" 事件: " + event);
System.out.println(" 事件的数据:" + event.getSource());
}
}
或者
package com.example.event;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
public class MyListener {
@EventListener({MyEvent.class})
public void abc(ApplicationEventevent) {
System.out.println("监听器: " + "MyListener");
System.out.println(" 线程: " + Thread.currentThread().getName());
System.out.println(" 事件: " + event);
System.out.println(" 事件的数据: " + event.getSource());
}
}
上边的办法比较好,因为不需要类型转换了。直接就能确定是MyEvent类型。
监听多个事件
事件进来之后,可以使用if(event instanceOf MyEvent.class)来判断是哪种事件。
package com.example.event;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyListener {
@EventListener({MyEvent.class, MyEvent2.class})
public void abc(ApplicationEvent event) {
System.out.println("监听器: " + "MyListener");
System.out.println(" 所在线程: " + Thread.currentThread().getName());
System.out.println(" 事件: " + event);
System.out.println(" 事件的数据:" + event.getSource());
}
}
监听所有ApplicationEvent
若使用这种写法,启动时会打印很多Spring自带的事件。任意ApplicationEvent都会进入这里边。
package com.example.event;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyListener {
@EventListener
public void abc(ApplicationEvent event) {
System.out.println("监听器: " + "MyListener");
System.out.println(" 所在线程: " + Thread.currentThread().getName());
System.out.println(" 事件: " + event);
System.out.println(" 事件的数据:" + event.getSource());
}
}
法2:实现ApplicationListener
public class MyListener implements ApplicationListenerSmartApplicationListener{ public void onApplicationEvent(MyEvent event){ System.out.println("监听器: " + "MyListener"); System.out.println(" 所在线程: " + Thread.currentThread().getName()); System.out.println(" 事件: " + event); System.out.println(" 事件的数据:" + event.getSource()); } }
源码如下
public interface SmartApplicationListener extends ApplicationListener, Ordered {
boolean supportsEventType(Class extends ApplicationEvent> var1);
default boolean supportsSourceType(@Nullable Class> sourceType) {
return true;
}
default int getOrder() {
return 2147483647;
}
}
supportsEventType:支持的事件的类型
supportsSourceType:支持的数据的类型
getOrder:2147483641:是2^31-1,也就是32位的int的最大正数 。
示例
事件
package com.example.event;
import org.springframework.context.ApplicationEvent;
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
监听器1
package com.example.event;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class MyListener implements SmartApplicationListener {
@Override
public boolean supportsEventType(Class extends ApplicationEvent> aClass) {
return aClass == MyEvent.class;
}
@Override
public boolean supportsSourceType(Class> sourceType) {
return sourceType == String.class;
}
@Override
public int getOrder() {
return 2;
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("监听器: " + "MyListener");
System.out.println(" 所在线程: " + Thread.currentThread().getName());
System.out.println(" 事件: " + event);
System.out.println(" 事件的数据:" + event.getSource());
System.out.println(" 是MyEvent?:" + (event instanceof MyEvent));
}
}
监听器2
package com.example.event;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class MyListener2 implements SmartApplicationListener {
@Override
public boolean supportsEventType(Class extends ApplicationEvent> aClass) {
return aClass == MyEvent.class;
}
@Override
public boolean supportsSourceType(Class> sourceType) {
return sourceType == String.class;
}
@Override
public int getOrder() {
return 1;
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("监听器: " + "MyListener2");
System.out.println(" 所在线程: " + Thread.currentThread().getName());
System.out.println(" 事件: " + event);
System.out.println(" 事件的数据:" + event.getSource());
System.out.println(" 是MyEvent?:" + (event instanceof MyEvent));
}
}
发布器
package com.example.event;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class MyPublisher {
@Autowired
ApplicationContext applicationContext;
public void myPublish(String message) {
System.out.println("发布器所在线程:" + Thread.currentThread().getName());
applicationContext.publishEvent(new MyEvent(message));
}
}
测试
package com.example.controller;
import com.example.event.MyPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
MyPublisher myPublisher;
@GetMapping("/test1")
public String test1() {
myPublisher.myPublish("Hello");
return "test1 success";
}
}
启动后,访问:http://localhost:8080/test1
后端输出:
发布器所在线程:http-nio-8080-exec-2 监听器: MyListener2 所在线程: http-nio-8080-exec-2 事件: com.example.event.MyEvent[source=Hello] 事件的数据:Hello 是MyEvent?:true 监听器: MyListener 所在线程: http-nio-8080-exec-2 事件: com.example.event.MyEvent[source=Hello] 事件的数据:Hello 是MyEvent?:true
如果将监听器的实现的Ordered顺序颠倒,则输出结果如下:
发布器所在线程:http-nio-8080-exec-1 监听器: MyListener 所在线程: http-nio-8080-exec-1 事件: com.example.event.MyEvent[source=Hello] 事件的数据:Hello 是MyEvent?:true 监听器: MyListener2 所在线程: http-nio-8080-exec-1 事件: com.example.event.MyEvent[source=Hello] 事件的数据:Hello 是MyEvent?:true注册监听器
| 方式 | 适用范围 | 能否搭配@Async注解,进行异步监听 |
| @Component | 所有监听器 | 能 |
| application.yml中添加配置 | 实现ApplicationListener | 不能 |
| 启动类中注册 | 实现ApplicationListener | 不能 |
法1:@Component(适用于所有监听器)
package com.example.event;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyListener {
@EventListener
public void abc(MyEvent event) {
System.out.println("监听器:" + "MyListener");
System.out.println("线程:" + Thread.currentThread().getName());
System.out.println("事件:" + event);
System.out.println("事件的数据:" + event.getSource());
}
}
package com.example.event; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; @Component public class MyListener2 implements ApplicationListener{ @Override public void onApplicationEvent(MyEvent event) { System.out.println("监听器:" + "MyListener2"); System.out.println("线程:" + Thread.currentThread().getName()); System.out.println("事件:" + event); System.out.println("事件的数据:" + event.getSource()); } }
法2:application.yml中添加配置(只适用于实现ApplicationListener
context:
listener:
classes: com.example.event.MyListener,com.example.event.MyListener2
法3:启动类中注册(只适用于实现ApplicationListener
package com.example;
import com.example.event.MyListener;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
// 原来是这样的:SpringApplication.run(DemoApplication.class, args);
ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);
context.addApplicationListener(new MyListener());
}
}
发布事件
法1:注入ApplicationContext,调用其publishEvent方法
package com.example.event;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class MyPublisher {
@Autowired
ApplicationContext applicationContext;
public void myPublish(String message) {
applicationContext.publishEvent(new MyEvent(message));
}
}
法2:启动类中发布
package com.example;
import com.example.event.MyEvent;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
//原来是:SpringApplication.run(DemoApplication.class, args);
ConfigurableApplicationContext context =SpringApplication.run(DemoApplication.class, args);
context.publishEvent(new MyEvent("Hello"));
}
}
SpringBoot启动事件
其他网址
Spring Boot 启动事件和监听器,太强大了!_Java技术栈,分享最主流的Java技术-CSDN博客
启动事件(按先后顺序)
- ApplicationStartingEvent
- 这个事件在 Spring Boot 应用运行开始时,且进行任何处理之前发送(除了监听器和初始化器注册之外)。
- ApplicationEnvironmentPreparedEvent
- 这个事件在当已知要在上下文中使用 Spring 环境(Environment)时,在 Spring 上下文(context)创建之前发送。
- ApplicationContextInitializedEvent
- 这个事件在当 Spring 应用上下文(ApplicationContext)准备好了,并且应用初始化器(ApplicationContextInitializers)已经被调用,在 bean 的定义(bean definitions)被加载之前发送。
- ApplicationPreparedEvent
- 这个事件是在 Spring 上下文(context)刷新之前,且在 bean 的定义(bean definitions)被加载之后发送。
- ApplicationStartedEvent
- 这个事件是在 Spring 上下文(context)刷新之后,且在 application/ command-line runners 被调用之前发送。
- AvailabilityChangeEvent
- 这个事件紧随上个事件之后发送,状态:ReadinessState.CORRECT,表示应用已处于活动状态。
- ApplicationReadyEvent
- 这个事件在任何 application/ command-line runners 调用之后发送。
- AvailabilityChangeEvent
- 这个事件紧随上个事件之后发送,状态:ReadinessState.ACCEPTING_TRAFFIC,表示应用可以开始准备接收请求了。
- ApplicationFailedEvent
- 这个事件在应用启动异常时进行发送。
上面所介绍的这些事件列表仅包括绑定到 SpringApplication 的 SpringApplicationEvents 事件,除了这些事件以外,以下事件也会在 ApplicationPreparedEvent 之后和 ApplicationStartedEvent 之前发送:
- WebServerInitializedEvent
- 这个 Web 服务器初始化事件在 WebServer 启动之后发送,对应的还有 ServletWebServerInitializedEvent(Servlet Web 服务器初始化事件)、ReactiveWebServerInitializedEvent(响应式 Web 服务器初始化事件)。
- ContextRefreshedEvent
- 这个上下文刷新事件是在 Spring 应用上下文(ApplicationContext)刷新之后发送。
源码分析
myPublisher.myPublish("Hello") //HelloController
applicationContext.publishEvent(new MyEvent(message)); //MyPublisher
publishEvent(event, (ResolvableType)null); //AbstractApplicationContext.class
// AbstractApplicationContext
getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
// AbstractApplicationContext
applicationEventMulticaster.multicastEvent((ApplicationEvent)applicationEvent, eventType)
SimpleApplicationEventMulticaster#multicastEvent
以下都在SimpleApplicationEventMulticaster
multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType)
// 此时,type为:com.example.tmp.MyEvent
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
for (ApplicationListener> listener : getApplicationListeners(event, type))
invokeListener(listener, event)
AbstractApplicationEventMulticaster#getApplicationListeners
getApplicationListeners(event, type))
retriever = new ListenerRetriever(true);
Collection> listeners = retrieveApplicationListeners(eventType, sourceType, retriever);
listeners = new linkedHashSet<>(this.defaultRetriever.applicationListeners);
遍历listeners,如果监听器支持此事件,则加入集合并返回。
事件监听(基于SpringBoot示例)_JustryDeng-CSDN博客
深入浅出Spring/SpringBoot 事件监听机制 - 知乎
Springboot事件机制整合EventBus应用(事件驱动模型)_fw19940314的博客-CSDN博客
SpringBoot事件监听_Chavaer-CSDN博客
SpringBoot-事件监听的4种实现方式_ignorewho的博客-CSDN博客_springboot事件监听



