事件监听机制(五)再话Jdk事件监听到Spring框架事件监听

r囧r小猫 2022-09-06 00:13 435阅读 0赞

事件监听机制(五)再话Jdk事件监听到Spring框架事件监听

背景

熟悉Spring 框架的同学都清楚,Spring容器启动到销毁事件监听机制始终贯穿,那么Jdk中是否也存在相应的监听机制呢,我们一起进行深入的学习和研究。

概念

事件源

事件对象的产生者,任何一个事件都有一个来源。

事件监听器注册表

当事件框架或组件收到一个事件后,需要通知所有相关的事件监听器来进行处理,这个时候就需要有个存储监听器的地方,也就是事件监听器注册表。

事件广播器

事件广播器在整个事件机制中扮演一个中介的角色,当事件发布者发布一个事件后,就需要通过广播器来通知所有相关的监听器对该事件进行处理。

Jdk实现

用户实体

  1. public class User {
  2. private Long id;
  3. private String name;
  4. public Long getId() { return id;}
  5. public void setId(Long id) { this.id = id;}
  6. public String getName() { return name;}
  7. public void setId(String name) { this.name= name;}
  8. public User(Long id, String name) { this.id = id;this.name= name;}
  9. }

用户事件

  1. public class UserEvent extends EventObject {
  2. public UserEvent(Object source) { super(source);}
  3. }

消息事件

  1. public class MsgEvent extends UserEvent{
  2. public MsgEvent(Object source) { super(source);}
  3. }

用户监听器

  1. public interface UserListener extends EventListener {
  2. // 处理用户注册事件
  3. void onRegister(UserEvent event);
  4. }

消息监听器

  1. public class MsgListener implements UserListener {
  2. @Override
  3. public void onRegister(UserEvent event) {
  4. if (event instanceof MsgEvent){
  5. User user = (User) event.getSource();
  6. System.out.println("向User id: " + user.getId() + "User name: " + user.getName() + "的用户发送消息");
  7. }
  8. }
  9. }

用户服务

  1. public class UserService {
  2. // 监听器列表
  3. private final List<UserListener> listeners = new ArrayList<>();
  4. // 添加监听器
  5. public void addListener(UserListener userListener){
  6. this.listeners.add(userListener);
  7. }
  8. // 注册用户并发布用户注册事件
  9. public void register(User user){
  10. System.out.println("注册用户,id为:" + user.getId());
  11. publishEvent(new MsgEvent(user));
  12. }
  13. // 广播用户注册事件
  14. private void publishEvent(UserEvent event){
  15. for (UserListener listener : listeners) {
  16. listener.onRegister(event);
  17. }
  18. }
  19. public static void main(String[] args) {
  20. UserService userService = new UserService();
  21. User user = new User(1L,sxs);
  22. userService.addListener(new MsgListener());
  23. userService.register(user);
  24. }
  25. }
  26. // 注册用户,id为:1
  27. // 向User id: 1 User name: sxs的用户发送消息

Spring实现

ApplicationEvent

Spring事件的顶层抽象类,代表事件本身,继承自JDKEventObjectSpring提供了一些内置事件,如ContextClosedEventContextStartedEventContextRefreshedEventContextStopedEvent等。

ApplicationListener

Spring事件监听器顶层接口,所有的监听器都实现该接口,继承自JDKEventListener
Spring提供了一些易扩展的接口,如SmartApplicationListenerGenericApplicationListener
ApplicationEventPublisherSpring的事件发布接口,事件源通过该接口的publishEvent方法发布事件,所有的应用上下文都具备事件发布能力,因为ApplicationContext继承了该接口。

ApplicationEventMulticaster

Spring事件机制中的事件广播器,它默认提供一个SimpleApplicationEventMulticaster实现,如果用户没有自定义广播器,则使用默认的(初始化逻辑见AbstractApplicationContext#refresh方法)。它通过父类AbstractApplicationEventMulticaster#getApplicationListeners方法从事件注册表中获取事件监听器,并且通过invokeListener方法执行监听器的具体逻辑。

执行逻辑

默认的广播器是同步调用监听器的执行逻辑,但是可以通过为广播器配置Executor实现监听器的异步执行。

事件广播器

Spring中通常是ApplicationContext本身担任监听器注册表的角色,在其子类AbstractApplicationContext中就聚合了事件广播器ApplicationEventMulticaster和事件监听器ApplicationListnener,并且提供注册监听器的addApplicationListnener方法。

事件发布

当一个事件源产生事件时,它通过事件发布器ApplicationEventPublisher发布事件,然后事件广播器ApplicationEventMulticaster会去事件注册表ApplicationContext中找到事件监听器ApplicationListnener,并且逐个执行监听器的onApplicationEvent方法,从而完成事件监听器的逻辑。

MailSendEvent

  1. public class MailSendEvent extends ApplicationContextEvent {
  2. private String to; //目的地
  3. public MailSendEvent(ApplicationContext source, String to) {
  4. super(source);
  5. this.to = to;
  6. }
  7. public String getTo(){
  8. return this.to;
  9. }
  10. }

MailSendListener

  1. @Component
  2. public class MailSendListener implements ApplicationListener<MailSendEvent>{
  3. @Override
  4. public void onApplicationEvent(MailSendEvent mailSendEvent) {
  5. MailSendEvent event = mailSendEvent;
  6. System.out.println("MailSender向"+ event.getTo()+ "发送了邮件");
  7. }
  8. }

MailSender

  1. @Component("mailSender")
  2. public class MailSender {
  3. @Autowired
  4. private ApplicationContext applicationContext; //容器事件由容器触发
  5. public void sendMail(String to){
  6. System.out.println("MailSender开始发送邮件");
  7. MailSendEvent event = new MailSendEvent(applicationContext,to);
  8. applicationContext.publishEvent(event);
  9. }
  10. }

SpringEventTest

  1. public class SpringEventTest {
  2. public static void main(String[] args) {
  3. // method1
  4. ApplicationContext context = new ClassPathXmlApplicationContext("web/WEB-INF/applicationContext.xml");
  5. context .refresh();
  6. MailSender sender = (MailSender)context.getBean("mailSender");
  7. sender.sendMail("上海");
  8. // method2
  9. AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
  10. ctx.register(AppConfig.class);
  11. ctx.refresh();
  12. MailSender sender = (MailSender)ctx.getBean("mailSender");
  13. sender.sendMail("石庄");
  14. }
  15. }

SpringBoot实现

SpringApplicationEventSpringBoot事件的顶层抽象类,且SpringBoot内置了7个事件:

ApplicationStartingEvent

SpringBoot启动开始的时候发布的事件。

ApplicationEnvironmentPreparedEvent

SpringBoot对应Environment准备完毕时发布的事件,此时上下文还没有创建,该事件中可以获取配置信息。通过监听该事件可以修改默认的配置信息,配置中心就是借助该事件完成将远端配置放入应用中。

ApplicationContextInitializedEvent

当准备好上下文,并且在加载任何Bean之前发布的事件。该事件可以获取上下文信息。

ApplicationPreparedEvent

上下文创建完成时发布的事件,此时的Bean还没有完全加载完成,只是加载了部分特定Bean。该事件可以获取上下文信息,但是无法获取自定义Bean,因为还没有被加载。

ApplicationStartedEvent

上下文刷新(完成所有Bean的加载)之后,但是还没有调用任何ApplicationRunnerCommandLineRunner运行程序之前发布的事件。该事件同样可以获取上下文信息,并且可以获取自定义的Bean

ApplicationReadyEvent

完成ApplicationRunnerCommandLineRunner运行程序调用之后发布的事件,此时的SpringBoot已全部启动完成。该事件同样可以获取上下文信息。

ApplicationFailedEvent

SpringBoot启动异常时发布的事件。该事件可以获取上下文信息和异常信息,通过该事件可以友好地完成资源的回收。

程序入口

SpringApplication.run(......)
SpringBoot的生命周期如下接口所示,会在各个生命周期广播响应的事件,调用实际的监听器。

  1. public interface SpringApplicationRunListener {
  2. // ApplicationStartingEvent
  3. default void starting() {
  4. }
  5. // ApplicationEnvironmentPreparedEvent
  6. default void environmentPrepared(ConfigurableEnvironment environment) {
  7. }
  8. // ApplicationContextInitializedEvent
  9. default void contextPrepared(ConfigurableApplicationContext context) {
  10. }
  11. // ApplicationPreparedEvent
  12. default void contextLoaded(ConfigurableApplicationContext context) {
  13. }
  14. // ApplicationStartedEvent
  15. default void started(ConfigurableApplicationContext context) {
  16. }
  17. // ApplicationReadyEvent
  18. default void running(ConfigurableApplicationContext context) {
  19. }
  20. // ApplicationFailedEvent
  21. default void failed(ConfigurableApplicationContext context, Throwable exception) {
  22. }
  23. }

上述接口提供了唯一的实现类EventPublishingRunListener,该类聚合了SpringApplicationSimpleApplicationEventMulticaster,前4个事件的发布由广播器完成,后3个委托ApplicationContext完成发布,其实最终都是由广播器负责发布。

监听器使用方式
1、通过SPI机制配置
resources目录下新建META-INF目录,并创建名为spring.factories的文件,在文件中声明以ApplicationListener接口全路径为键的配置,配置内容为ApplicationListener实现类的全路径,若有多个以逗号间隔,示例如下:

  1. org.springframework.context.ApplicationListener=\
  2. com.lyentech.bdc.tuya.listener.HunterApplicationContextInitializedListener,\
  3. com.lyentech.bdc.tuya.listener.HunterApplicationEnvironmentPreparedListener,\
  4. com.lyentech.bdc.tuya.listener.HunterApplicationFailedListener,\
  5. com.lyentech.bdc.tuya.listener.HunterApplicationPreparedListener,\
  6. com.lyentech.bdc.tuya.listener.HunterApplicationReadyListener,\
  7. com.lyentech.bdc.tuya.listener.HunterApplicationStartedListener,\
  8. com.lyentech.bdc.tuya.listener.HunterApplicationStartingListener

Spring在启动的时候会加载spring.factories中配置的监听器实现类,并在合适的阶段通过发布事件触发相应监听器的逻辑。此方式可以实现针对任何事件(SpringBoot内置7个事件都支持)监听器的成功加载。
2、注解实现

  1. @Component
  2. public class CustomizeListener {
  3. @EventListener
  4. public void listenerApplicationStartingEvent(ApplicationStartingEvent event){
  5. System.err.println("ApplicationStartingEvent事件");
  6. }
  7. @Async // 异步支持,需要开启@EnableAsync
  8. @EventListener
  9. public void listenerApplicationStartedEvent(ApplicationStartedEvent event){
  10. System.err.println("ApplicationStartedEvent事件");
  11. }
  12. @EventListener
  13. public void listenerApplicationReadyEvent(ApplicationReadyEvent event){
  14. System.err.println("ApplicationReadyEvent事件");
  15. }
  16. }

总结

以上为事件监听机制的详细实现,由此可实现代码解耦,便于模块化开发,相关的MQ中间件设计,也是基于此思想,对于理解消息解耦、监听者模式等有很大帮助。

更多文章

事件监听机制(一)Java事件监听
https://blog.csdn.net/shang\_xs/article/details/87911756
事件监听机制(二)Spring事件监听
https://blog.csdn.net/shang\_xs/article/details/88048545
事件监听机制(三)Spring Cloud Bus流程分析
https://blog.csdn.net/shang\_xs/article/details/88050196
事件监听机制(四)从Java事件监听到Spring事件监听
https://blog.csdn.net/shang\_xs/article/details/86560994
事件监听机制(五)再话Jdk事件监听到Spring框架事件监听
https://blog.csdn.net/shang\_xs/article/details/119794917

发表评论

表情:
评论列表 (有 0 条评论,435人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Spring事件监听机制

    前言 Spring中的事件机制其实就是设计模式中的观察者模式,主要由以下角色构成: 1. 事件 2. 事件监听器(监听并处理事件) 3. 事件发布者(发布事件...

    相关 事件监听机制

    ![这里写图片描述][20160420164724788]   Java中的事件监听是整个Java消息传递的基础和关键。牵涉到三类对象:事件源(Event S