Spring的事件监听机制示例详解
作者:jy的blog 发布时间:2023-01-25 18:40:42
前言
最近公司在重构广告系统,其中核心的打包功由广告系统调用,即对apk打包的调用和打包完成之后的回调,需要提供相应的接口给广告系统。因此,为了将apk打包的核心流程和对接广告系统的业务解耦,利用了spring的事件监听特性来满足需求。以下说明spring的事件机制的相关内容。
首先spring事件分为事件发布者(EventPublisher)、事件监听者(EventListener),还包括一个事件广播者(这个是spring实现相关,这一节不讨论)。使用spring事件机制,需要自定义事件发布者和监听者。
1.观察者模式
Spring的事件监听(也称事件驱动)是观察者模式的一种实现,比较常见的有发布-订阅模型。通常我们利用消息队列来实现不同系统之间的解耦,如用户注册完成后,可以向消息队列发布一条消息,然后订阅了此topic的子系统(如邮件服务,积分服务)收到发布的消息之后,就会做相应的处理。这样做的好处是避免了在注册服务里耦合其他服务的代码,并且,执行子系统的业务将会异步执行,互不影响。下图是一个经典的观察者模式的结构。
以下为上述观察者模式的java简单实现:
(1)Subject.java
package observerPattern;
import java.util.ArrayList;
import java.util.List;
/**
* Created by jy on 2018/11/28.
*/
public abstract class Subject {
//维护一个所有观察者集合
private List<Observer> list = new ArrayList<>();
//新注册一个观察者
public void attach(Observer observer){
list.add(observer);
System.out.println("新注册一个观察者");
}
//删除一个已注册的观察者
public void detach(Observer observer){
list.remove(observer);
System.out.println("删除一个已注册的观察者");
}
//通知所有已经注册的观察者
public void notifyObservers(String state){
for (int i = 0; i < list.size(); i++) {
list.get(i).update(state);
}
}
}
(2)Observer.java
package observerPattern;
/**
* Created by jy on 2018/11/28.
*/
public interface Observer {
// 抽象出的更新行为
public void update(String state);
}
(3)ConcreteSubject.java
package observerPattern;
/**
* Created by jy on 2018/11/28.
*/
public class ConcreteSubject extends Subject{
//真实主题内维护一个状态
private String state;
public String getState() {
return state;
}
public void change(String state){
this.state = state;
System.out.println("真实主题状态变化为:"+state);
this.notifyObservers(state);
}
}
(4)ConcreteObserver.java
package observerPattern;
/**
* Created by jy on 2018/11/28.
*/
public class ConcreteObserver implements Observer {
//具体观察者的状态
private String observerState;
@Override
public void update(String state) {
//这里可以根据传递过来的主题的状态作出相应的业务
observerState = state;
System.out.println("观察者的状态跟着变化为:"+observerState);
}
}
(5)Main.java
package observerPattern;
/**
* Created by jy on 2018/11/28.
*/
public class Main {
public static void main(String[] args) {
//真实主题
ConcreteSubject concreteSubject = new ConcreteSubject();
//真实观察者
ConcreteObserver concreteObserver = new ConcreteObserver();
//观察者先注册
concreteSubject.attach(concreteObserver);
//改变真实主题状态
concreteSubject.change("2");
}
}
结果:在执行了main方法之后,我们可以看到控制台输出结果,表明,真实观察者的状态是会根据真实主题的状态变化而变化的:
2. Spring事件监听
spring也对事件驱动模型提供了支持,该模型主要由三部分组成:
(1) 事件(ApplicationEvent):继承了jdk的EventObject,在spring项目中可以继承ApplicationEvent,来自定义自己的事件。
spring容器内部对ApplicationEvent有着下面几个实现,通过名字可以很清楚事件所描述的行为。
(2)发布者(ApplicationEventPublisher):实现这个接口,就可以使得spring组件有发布事件的能力。
可以看到,ApplicationContext实现了此接口,因此,可以spring组件可以通过实现ApplicationContextAware接口,注入ApplicationContext,然后,通过ApplicationContext的publishEvent()方法来实现事件传播,
当然,也可以直接实现ApplicationEventPublisher接口,重写publishEvent()方法,同样可以实现事件传播。
通过阅读源码发现,在AbstractApplicationContext类中,定义了针对观察者的增加,get,注册等方法。下面代码中的addApplicationListener()是向ApplicationEventMulticaster类中维护的一个set中添加listener。这个set存储了该发布者所有的观察者(listener)。
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
Assert.notNull(listener, "ApplicationListener must not be null");
//listener传入持有的一个的applicationEventMulticaster类中
if (this.applicationEventMulticaster != null) {
this.applicationEventMulticaster.addApplicationListener(listener);
}
this.applicationListeners.add(listener);
}
//省略部分代码
protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// Publish early application events now that we finally have a multicaster...
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
在AbstractApplicationContext中publishEvent:
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
//.....
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); //事件广播
//....
}
具体的发布事件的方法都在上面提到的ApplicationEventMulticaster这个类型的类中去实现的,在AbstractApplicationContext中,会先尝试从ConfigurableListableBeanFactory中去加载这个类,如果不存在,则会默认new 一个SimpleApplicationEventMulticaster:
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) { //尝试加载
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory); //不存在则默认使用SimpleApplicationEventMulticaster
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
看看SimpleApplicationEventMulticaster 是怎么广播事件的,由代码可知,在线程池不为空的情况下,异步发布特定类型的事件。
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
//....
将invokeListener方法点击到最后,发现调用了listener的onApplicationEvent(),实现了事件的发布。
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
//....
}
}
(3)事件订阅者(ApplicationListener):实现这个接口,就可以监听ApplicationListener发布的特定的事件。
实现ApplicationListener这个接口,重写onApplicationEvent()方法,来处理监听到的ApplicationEvent,这里可以监听特定类型的事件。
3. 基于注解的事件监听
spring也为发布者和监听者提供了相应的注解支持,只需要在对应的观察者类的对应方法上加上@EventListener:
对于发布者,可以直接在service通过@Autowired注入ApplicationEventPublisher。
4.小结
文章主要介绍了spring中事件驱动的模型。主要运用了观察者模式的思想,随后介绍了spring中事件发布的机制。
来源:https://www.cnblogs.com/jy107600/p/10034857.html


猜你喜欢
- 本文将反射的东西整理了一下 , 提供了最全面的东西 , 当然也是基础的东西 ,在学好了这一切的基础上 , 大家可以学习反射的具体插件等应用
- 简单来说抽象类通常用来作为一个类族的最顶端的父类,用最底层的类表示现实中的具体事物,用最顶层的类表示该类族所有事物的共性。用abstract
- 如题,市面上常见的方法是:var handle = bmp.GetHicon(); //得到图标句柄return Icon.Fr
- 微信公众平台(map.weixin.qq.com)/开放平台(open.weixin.qq.com)/商户平台(pay.weixin.qq.
- 本文实例讲述了Java求解两个非负整数最大公约数算法。分享给大家供大家参考,具体如下:代码功能:1.Java实现(完整源码附测试用例);2.
- 本文实例为大家分享了C#实现会移动的文字效果的具体代码,供大家参考,具体内容如下1 题目描述(1)Form1窗体设计界面如下:(2)窗体左侧
- 验证码逻辑以前在项目中也做过验证码,生成验证码的代码网上有很多,也有一些第三方的jar包也可以生成漂亮的验证码。验证码逻辑很简单,就是在登录
- foreach 循环 list(map)直接上代码:整体需求就是1.分页对象里面有map map里面又有数组对象2.分页对象里面有list
- 当你在更改后台更新频率来减少这些更新对电池寿命的影响时,检查当前电量和充电状态是一个好的开始。电池寿命通过剩余电量和充电状态来影响应用更新的
- 笔者计划为大家介绍分布式文件系统,用于存储应用的图片、word、excel、pdf等文件。在开始介绍分布式文件系统之前,为大家介绍一下使用本
- 前言 CompoundButton在XML文件中主要使用下面两个属性。checked:指定按钮的勾选状态,true表示勾选,fal
- 前面有文章介绍了使用GridView实现表格的方法,本文就来说说如何用ListView实现自适应的表格。GridView比ListView更
- 如果我们有这样一个应用场景:WidgetA执行点击之后将数据通过widgetB传递到其下的widgetC。通常可以通过设置构造函数,传递对应
- 一、Thymeleaf简介Thymeleaf是面向Web和独立环境的现代服务器Java模板引擎,能够处理HTML,XML,JavaScrip
- 本文介绍了Android ListView 实现上拉加载的示例代码,分享给大家,具体如下:我们先分析一下如何实现 ListView 上拉加载
- Object是所有类的父类,也就是说java中所有的类都是直接或者间接继承自Object类。比如你随便创建一个classA,虽然没有明说,但
- 1.Context的应用Context在启动协程模式中就已经遇到过叫CoroutineContext,它的意思就是协程上下文,线程的切换离不
- 本文实例讲述了Java * 和AOP应用。分享给大家供大家参考,具体如下:一 点睛 * 在AOP(Aspect Orient Progr
- using System;using System.Diagnostics;using System.Runtime.InteropServ
- 介绍装饰模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。就功能而言,装饰模式相比生成子类更为灵活,这样可以给某个对象