原文网址: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注册监听器
法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事件监听
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)