首页 > 其他分享 >Spring5 IOC容器解析——事件监听机制

Spring5 IOC容器解析——事件监听机制

时间:2023-01-07 18:31:22浏览次数:63  
标签:Spring void 观察者 event 事件 IOC 监听 Spring5

一、事件驱动模型简介

事件驱动模型,也即是我们通常说的观察者。基于发布-订阅模式的编程模型。

概念

定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都得到通知并自动更新。

百度百科: 从事件角度说,事件驱动程序的基本结构是由一个事件收集器、一个事件发送器和一个事件处理器组成。 事件收集器专门负责收集所有事件,包括来自用户的(如鼠标、键盘事件等)、来自硬件的(如时钟事件等)和来自软件的(如操作系统、应用程序本身等)。 事件发送器负责将收集器收集到的事件分发到目标对象中。 事件处理器做具体的事件响应工作。

从程序设计的角度来看,事件驱动模型的核心构件通常包含以下几个:

  • 事件源(Event Source):负责产生事件的对象。比如我们常见的按钮,按钮就是一个事件源,能够产生“点击”这个事件
  • 事件监听器(Event Listener):注册在事件源上才能被调用,主要用于监听事件并进行事件处理或者转发。
  • 事件对象(Event Object):或者称为事件对象,是事件源和事件监听器之间的信息桥梁。是整个事件模型驱动的核心

下图展示了事件、事件源、监听器直接的关系:

1、 观察者模式

原理解析

观察者模式的UML图如下:

具体类说明如下:

  • Observer观察者:即为事件模型中的事件监听器,该接口定义一个方法update,即为事件处理方法。当观察到有事件产生时,该方法便会处理
  • Subject被观察者:即事件模型中的事件源,负责产生事件,定义与观察者建立关联的方法(添加观察者、删除观察者、通知观察者)
  • ConreteObserver具体的观察者实现类:实现Observer接口中的update方法,具体实例会被添加到被观察者的观察者队列中(observers[List])
  • ConreteSubject具体的被观察者实现类:实现Subject接口。定义观察者队列(observers[List]),并定义实现如何将观察者对象添加到观察者队列中以及如何通所有知观察者

在上述类图中,具体的ConreteSubject被观察者,其中包含observers一个列表,保存所有观察者对象。doAction方法是需要通知观察者对象的动作,当该方法执行后,会通知保存在observers中的所有观察者。

 

也就是在执行方法ConreteSubject#doAction()时,需要调用ConreteSubject#notifyObservers()通知保存在observers中的所有观察者,让其能够做出响应。

Spring 中的事件监听机制

Spring 中的事件

Spring 中的事件通知机制就是观察者模式的一种实现。观察者是 ApplicationListener,可以实现接口定义观察者,也可以使用注解定义观察者。观察者感兴趣的是某种状态的变化,这种状态变化使用 ApplicationEvent 来传达,也就是事件对象。我们说的 Spring 中的事件,就是 ApplicationEvent。在事件中,被观察者可以认为是发出事件的一方,只有在状态变化时才发布事件。

 

当有状态发生变化时,发布者调用 ApplicationEventPublisher 的 publishEvent 方法发布一个事件,Spring 容器广播事件给所有观察者,调用观察者的 onApplicationEvent 方法把事件对象传递给观察者。调用 publishEvent 方法有两种途径,一种是实现接口由容器注入 ApplicationEventPublisher 对象然后调用其方法,另一种是直接调用容器的方法,两种方法发布事件没有太大区别。

 

相关类型总结如下:

  • ApplicationListener:事件监听者,观察者;
  • ApplicationEvent:Spring 事件,记录事件源、时间和数据;
  • ApplicationEventPublisher:发布事件;

优势

使用事件可以解耦代码,观察者与被观察者可以分开开发,中间只有事件作为联系,不用关心另一方如何实现。观察者可以有多个,所以对于同一个事件可以有多种不同的处理方式,不过要确保不依赖处理的顺序。使用事件后,观察者可以单独开发,对主流程没有任何影响,可以简化主流程的开发。

 

事件可以用于各种场景的消息通知,比如系统收到停机指令后,可以发出停机事件,这样相关的子系统就可以进行停机前的各种处理。那么 Spring 中的事件是如何实现的呢?

Spring事件驱动模型的三大组成部分

  • ApplicationEvent:事件对象,Spring事件驱动模型中的对象源,继承JDK EventObject,通过在发布事件时通过EventObject.source字段携带事件相关的数据。

  • ApplicationListener:应用监听器,负责监听事件对象是否有发生变化,实现该接口并实现onApplicationEvent方法,完成事件发生变化时的逻辑处理

  • ApplicationEventPublisher:事件发布器,定义了事件发布规范,只定义了接口,具体的实现交由其他类中实现。Spring提供了SimpleApplicationEventMulticaster实现了广播事件发布器。

Spring 框架提供了四种容器事件,我们可以直接观察,包括:

  • ContextStartedEvent:ApplicationContext 启动事件。
  • ContextRefreshedEvent:ApplicationContext 更新事件。
  • ContextStoppedEvent:ApplicationContext 停止事件。
  • ContextClosedEvent:ApplicationContext 关闭事件。

Spring 4.2 之前的版本,事件必须继承 ApplicationEvent,从 Spring 4.2 版开始,框架提供了 PayloadApplicationEvent 用于包装任意类型,不强制事件对象继承 ApplicationEvent。当我们发送一个任意类型的事件对象时,框架内部自动包装为 PayloadApplicationEvent 事件对象。

事件监听者

事件监听者 ApplicationListener 继承自 JDK 的 EventListener ,JDK 要求所有监听者继承它。监听者只有一个方法 onApplicationEvent,用来处理事件 ApplicationEvent。

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

	/**
	 * Handle an application event.
	 * @param event the event to respond to
	 */
	void onApplicationEvent(E event);

}

在容器启动的时候检测应用中的监听者并把用户实现的监听者注册到 SimpleApplicationEventMulticaster 集合中。

 

Spring 也支持直接注解的形式进行事件监听,使用注解 @EventListener 即可。使用注解时,方法可以返回任意类型,如果返回值不为 void 则当做一个新事件再次发布。

@Service
public class AnnoEventListener {
    @EventListener
    public void listen(MyEvent myEvent){
        System.out.println("receive " + myEvent.getSource());
    }
}

注解形式的监听者是通过 EventListenerMethodProcessor 注册到容器中的。该类定义了一个 Bean,在初始化完成后,调用它的后置回调方法 afterSingletonsInstantiated,在方法中遍历容器中所有的 bean,提取出 EventListener 注解修饰的方法并根据注解的参数创建 ApplicationListener 对象加入到 ApplicationContext 监听列表中。

发布事件

ApplicationEventPublisher 接口定义了事件发布方法,代码如下:

@FunctionalInterface
public interface ApplicationEventPublisher {

	default void publishEvent(ApplicationEvent event) {
		publishEvent((Object) event);
	}

	void publishEvent(Object event);
}

ApplicationContext 接口继承了 ApplicationEventPublisher ,并在 AbstractApplicationContext 实现了具体代码,实际执行是委托给 ApplicationEventMulticaster。

ApplicationEventMulticaster 接口定义了对监听者的操作,如增加监听者、移除监听者,以及发布事件的方法。框架提供了 ApplicationEventMulticaster 接口的默认实现SimpleApplicationEventMulticaster,如果本地容器中没有 ApplicationEventMulticaster 的实现就会使用这个默认实现。

public interface ApplicationEventMulticaster {

	void addApplicationListener(ApplicationListener<?> listener);

	void addApplicationListenerBean(String listenerBeanName);

	void removeApplicationListener(ApplicationListener<?> listener);

	void removeApplicationListenerBean(String listenerBeanName);

	void removeAllListeners();

	void multicastEvent(ApplicationEvent event);

	void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}

在 SimpleApplicationEventMulticaster 中,可以看到 multicastEvent 方法中遍历了所有的 Listener,并依次调用 Listener 的 onApplicationEvent 方法发布事件。Spring 还提供了异步发布事件的能力,taskExecutor 不为 null 时即异步执行。

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

	@Nullable
	private Executor taskExecutor;

	@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		Executor executor = getTaskExecutor();
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				invokeListener(listener, event);
			}
		}
	}
}

注意事项: 可以看到,Spring 处理事件是同步的,监听者的执行时间最好不要太长,以免影响主流程。如果事件的处理一定要耗费比较长的时间,请使用异步方式进行处理。

高级监听者

SmartApplicationListener

SmartApplicationListener 接口是 ApplicationListener 的子接口,还继承了 Ordered 接口。SmartApplicationListener 定义了两个 support 方法用于判断事件类型、来源类型是否和当前监听者匹配,这样监听者可以筛选自己感兴趣的事件和来源。继承 Ordered 接口后,该监听者具备了排序的功能,可以按照 order 从小到大的顺序给监听者确定一个优先级,从而确保执行顺序。

public interface Ordered {

	int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;

	int LOWEST_PRECEDENCE = Integer.MAX_VALUE;

	int getOrder();
}


public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
	
	//指定支持哪些类型的事件
	//判断事件的类型是否和当前监听者匹配
	boolean supportsEventType(Class<? extends ApplicationEvent> eventType);

	//指定支持发生事件所在的类型
	//判断事件源类型是否和当前监听者匹配
	default boolean supportsSourceType(@Nullable Class<?> sourceType) {
		return true;
	}

	@Override
	default int getOrder() {
		return LOWEST_PRECEDENCE;
	}

}

GenericApplicationListener

GenericApplicationListener 接口是 ApplicationListener 的子接口,也继承了 Ordered 接口,同 SmartApplicationListener 一样具有事件筛选能力和排序能力。但筛选事件使用的是 ResolvableType 类型,而不是 ApplicationEvent 类型。

 

ResolvableType类型是Spring4之后添加进来的获取泛型信息的工具,通过ResolvableType可以获取到传入的泛型的各种信息。

public interface GenericApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {

	//指定支持哪些类型的事件
	//判断事件的类型是否和当前监听者匹配
	boolean supportsEventType(ResolvableType eventType);

	//指定支持发生事件所在的类型
	//判断事件源类型是否和当前监听者匹配
	default boolean supportsSourceType(@Nullable Class<?> sourceType) {
		return true;
	}

	@Override
	default int getOrder() {
		return LOWEST_PRECEDENCE;
	}

}

参考: https://blog.csdn.net/zrudong/article/details/78567473

https://zhuanlan.zhihu.com/p/40071652

https://blog.csdn.net/chenwiehuang/article/details/80641811

标签:Spring,void,观察者,event,事件,IOC,监听,Spring5
From: https://blog.51cto.com/u_14014612/5995714

相关文章

  • Spring5 IOC容器解析——BeanDefinitionReader
    概述BeanDefinitionReader的作用是读取Spring配置文件中的内容,将其转换为IOC容器内部的数据结构:BeanDefinition。在前面章节关于BeanDefinition的学习中有提到XmlB......
  • Spring5 IOC容器解析——BeanDefinition的注册
    前言在上一篇文章解析BeanDefinition对配置文件解析完成后,获取的beanDefiniton已经可以进行使用了,剩下的唯一工作就是注册了,也就是processBeanDefinition方法中的BeanDefi......
  • Spring IOC官方文档学习笔记(七)之Bean Definition继承
    1.BeanDefinition继承(1)Spring中的bean存在层级关系,我们可以定义子bean来继承或覆盖父bean中的某些属性,从而节省编码,在此处Spring运用到了模板设计模式,如下所示//自定......
  • vue2,vue3同时监听多个数据变化
    我是使用vue计算属性(computed)将需要监听的数据拼接在一起,再通过watch进行监听1.vue普通写法<template><divid="app"><el-inputv-model.number="valu......
  • 【Android】学习day05|简单登陆页面的实现|监听代码
    实现效果如下图所示    实现代码【部分】MainActivity.java1packagecom.example.app02;23importandroidx.appcompat.app.AppCompatActivity;4......
  • watch监听器
    watch的基本用法:  也可以用数组的方式同时监听多个 深度监听:使用ref需要开启深度监听才可以监听到里面的值使用reactive不需要开启也可以监听到里面的值,因为源......
  • netcore 容器内部监听设置localhost 外部无法访问
    情况1  由于localhost只能够在容器内部访问,所以在设置的时候改为*号,而不是固定ip。情况2  也可能由于只配置了容器和宿主机的映射,容器内部未监听端口号导致情况3......
  • 学习笔记——过滤器链;监听器;Servlet、Filter、Listener的注解方式开发
    2023-01-06一、过滤器链1、含义:如果出现一个请求存在多个过滤器对其过滤,出现过滤器链。在放行前,过滤器是正序执行,放行后过滤器是倒序执行。2、过滤器的顺序:是与filter-......
  • Java监听器实现原理
    文章目录​​监听器模型​​​​案例实现​​​​`DeveloperListener`​​​​`Developer`​​​​`Event`​​​​`DeveloperListenerImpl`​​​​测试​​监听器就是监听......
  • 监听input组件的内容改变,并进行实时响应
    监听input组件的内容改变,并进行实时响应危笑演绎总有人要赢的,那为什么不是你?  在开发中,有时我们需要对 input 的 value 值变化作实时响应。......