Spring 事件监听机制及原理分析
欢迎大家关注我的个人博客—精灵王,获取更好的阅读体验以及最新的文章分享~
简介
在JAVA体系中,有支持实现事件监听机制,在Spring 中也专门提供了一套事件机制的接口,方便我们实现。比如我们可以实现当用户注册后,给他发送一封邮件告诉他注册成功的一些信息,比如用户订阅的主题更新了,通知用户注意及时查看等。
观察者模式
观察者模式还有很多其他的称谓,如发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
观察者模式一般包含以下几个对象:
Subject:
被观察的对象。它提供一系列方法来增加和删除观察者对象,同时它定义了通知方法notify()。目标类可以是接口,也可以是抽象类或具体类。
ConcreteSubject:
具体的观察对象。Subject的具体实现类,在这里实现通知事件。
Observer:
观察者。这里是抽象的观察者,观察者有一个或者多个。
ConcreteObserver:
具体的观察者。在这里维护观察对象的具体操作。
Java 中的事件机制
Java中提供了基本的事件处理基类:
- EventObject:所有事件状态对象都将从其派生的根类;
- EventListener:所有事件侦听器接口必须扩展的标记接口;
非常经典的开门案例:
一、创建事件对象
二、事件监听器
三、测试
四、输出结果
Spring 中的事件机制
在 Spring 容器中通过ApplicationEven
类和 ApplicationListener
接口来实现事件监听机制,每次Event 被发布到Spring容器中时都会通知该Listener。需要注意的是,Spring 的事件默认是同步的,调用 publishEvent
方法发布事件后,它会处于阻塞状态,直到Listener接收到事件并处理返回之后才继续执行下去。
代码示例:
一、定义事件对象
二、定义事件监听器,可以通过注解或者实现接口来实现。
三、注册服务
四、测试
五、输出结果
指定监听器的顺序
监听器的发布顺序是按照 bean 自然装载的顺序执行的,Spring 支持两种方式来实现有序
一、实现SmartApplicationListener接口指定顺序。
把上面三个Listener都改成实现SmartApplicationListener接口,并指定getOrder的返回值,返回值越小,优先级越高。
另外两个监听器的改造省略,指定改造后的UserRegisterSmsListener返回order为0,UserRegisterEmailListener的getOrder返回1,测试输出结果如下:
二、使用注解@Order()
测试输出结果如下:
可以发现,短信监听器最先执行。
异步支持
Spring 事件机制默认是同步阻塞的,如果 ApplicationEventPublisher 发布事件之后他会一直阻塞等待listener 响应,多个 listener 的情况下前面的没有执行完后面的会一直被阻塞。这时候我们可以利用 Spring 提供的线程池注解 @Async
来实现异步线程
一、使用 @Async 之前需要先开启线程池,在 启动类上添加 @EnableAsync 注解即可。
二、监听器使用异步线程
自定义异步线程池
指定监听器的线程池
三、测试输出结果
Spring事件机制原理分析
Spring事件机制涉及的重要类主要有以下四个:
ApplicationEvent:
事件对象,继承至JDK的类EventObject
,可以携带事件的时间戳
ApplicationListener:
事件监听器,继承至JDK的接口EventListener
,该接口被所有的事件监听器实现,比如支持指定顺序的SmartApplicationListener
ApplicationEventMulticaster:
事件管理者,管理监听器和发布事件,ApplicationContext通过委托ApplicationEventMulticaster来 发布事件
ApplicationEventPublisher:
事件发布者,该接口封装了事件有关的公共方法,作为ApplicationContext的超级街廓,也是委托 ApplicationEventMulticaster完成事件发布。
源码展示
ApplicationEvent
事件对象ApplicationEvent的主要源代码如下,继承了JAVA的 EventObject 对象:
从上面ApplicationEvent的子类关系图种可以发现,ApplicationEvent有一个重要的子类ApplicationContextEvent
,而ApplicationContextEvent又有4个重要的子类ContextStartedEvent
、ContextRefreshedEvent
、ContextClosedEvent
、ContextStoppedEvent
。
从名字就可以看出,这4个事件都和Spring容器有关系的:
- ContextRefreshedEvent:当spring容器context刷新时触发
- ContextStartedEvent:当spring容器context启动后触发
- ContextStoppedEvent:当spring容器context停止时触发
- ContextClosedEvent:当spring容器context关闭时触发,容器被关闭时,其管理的所有单例Bean都被销毁。
当每个事件触发时,相关的监听器就会监听到相应事件,然后触发onApplicationEvent
方法。
ApplicationListener
事件监听器,继承DK的接口EventListener
注释@param <E> the specific ApplicationEvent subclass to listen to@see ApplicationEventMulticaster
里面说明了事件的广播在ApplicationEventMulticaster
类。
ApplicationEventMulticaster
ApplicationEventMulticaster
是一个接口,负责管理监听器和发布事件,定义了如下方法:
addApplicationListener(ApplicationListener<?> listener)
:新增一个listener;addApplicationListenerBean(String listenerBeanName)
:新增一个listener,参数为bean name;removeApplicationListener(ApplicationListener<?> listener)
:删除listener;void removeAllListeners()
:删除所有的ListenerremoveApplicationListenerBean(String listenerBeanName)
:根据bean name 删除listener;multicastEvent(ApplicationEvent event)
:广播事件;multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType)
:广播事件,指定事件的source类型。
AbstractApplicationEventMulticaster 实现了 ApplicationEventMulticaster接口,SimpleApplicationEventMulticaster 继承了AbstractApplicationEventMulticaster ;
-
AbstractApplicationEventMulticaster 主要实现了管理监听器的方法(上面接口的前5个方法)
-
SimpleApplicationEventMulticaster 主要实现了事件广播相关的方法(上面接口的最后2个方法)
两个类分别继承了部分上面的方法。
一、先看新增Listener方法实现逻辑:
最核心的一句代码:this.defaultRetriever.applicationListeners.add(listener);
ListenerRetriever类是AbstractApplicationEventMulticaster类的内部类,里面有两个集合,用来记录维护事件监听器。
这就和设计模式中的发布订阅模式一样了,维护一个List,用来管理所有的订阅者,当发布者发布消息时,遍历对应的订阅者列表,执行各自的回调handler。
二、看SimpleApplicationEventMulticaster类实现的广播事件逻辑:
代码分析:
- 首先根据事件类型,获取事件监听器列表:
getApplicationListeners(event, type)
- 遍历监听器列表,for循环
- 判断是否有线程池,如果有,在线程池执行
- 否则直接执行
我们再看看 invokeListener
方法的逻辑:
核心逻辑就是继续调用doInvokeListener
方法:
发现最后实际就是调用的 listener.onApplicationEvent(event);
也就是我们通过实现接口ApplicationListener的方式来实现监听器的onApplicationEvent实现逻辑。
ApplicationEventPublisher类
在我们的发布事件逻辑代码的地方,通过查看 eventPublisher.publishEvent(userDTO);
方法可以发现ApplicationEventPublisher是一个接口,publishEvent方法的逻辑实现主要在类AbstractApplicationContext中:
这段代码的主要逻辑在这:
可以发现earlyApplicationEvents也是一个Set集合,如果这个集合已经初始化了,就把事件加入到集合中,否则直接调用multicastEvent执行事件监听逻辑。
我们跟踪找到初始化这个集合的地方,发现在方法protected void prepareRefresh()
中:
继续跟踪调用这个方法的地方,发现在AbstractApplicationContext.refresh()
方法中,而这个方法是Spring容器初始化必须要调用的过程,非常的重要。
那在什么地方使用到了这个集合呢?我们继续跟踪发现在 protected void registerListeners()
方法中,代码如下:
逻辑是先获得该集合的引用,然后置空之前的集合,然后遍历集合,进行广播事件multicastEvent
,这个方法的逻辑上面已经说过了。
而registerListeners这个方法是在什么时候调用的呢?通过跟踪发现也是在AbstractApplicationContext.refresh()
方法中。
只不过基本是在方法逻辑的最后,也就是Spring已经容器初始化完成了。
容器初始化之前和之后都有可能进行广播事件。
总结
- 事件监听机制和观察者模式非常相似
- JDK 也有实现提供事件监听机制
- Spring 的事件机制也是基于JDK 来扩展的
- Spring 的事件机制默认是同步阻塞的
- Spring 容器初始化前后都可能进行广播事件
个人博客:精灵王
__EOF__

本文链接:https://www.cnblogs.com/admol/p/14036564.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
微信搜索公众号“jinglingwangcoding”或扫描下方二维码,一起交流

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端