当前位置: 首页 > news >正文

【spring】spring学习系列之十一:spring的事件监听

系列文章目录

文章目录

  • 系列文章目录
  • 前言
  • 一、使用
  • 二、整体流程
  • 三、EventListenerMethodProcessor和DefaultEventListenerFactory
    • 1.EventListenerMethodProcessor
    • 2.DefaultEventListenerFactory
    • 3.ApplicationListenerDetector
    • 4.initApplicationEventMulticaster
    • 5.registerListeners
    • 6.SimpleApplicationEventMulticaster
  • 四、事件发布过程
  • 总结


前言

Spring框架的事件机制(ApplicationEvent)提供了一种组件间通信的方式,允许应用程序的不同部分通过发布-订阅模式进行解耦交互。

一、使用

  1. 先定义事件
public class MyCustomEvent {private String message;public MyCustomEvent(String message) {this.message = message;}public String getMessage() {return message;}
}
  1. 注册事件监听器
    这里是通过@EventListener的方式,也可以通过实现ApplicationListener接口的方式
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;@Component
public class MyEventListener {// 监听特定类型的事件@EventListenerpublic void handleMyEvent(MyCustomEvent event) {System.out.println("Received MyCustomEvent - " + event.getMessage());}// 监听多个事件类型@EventListener({AnotherEvent.class, YetAnotherEvent.class})public void handleMultipleEvents(Object event) {if (event instanceof AnotherEvent) {System.out.println("Handling AnotherEvent");} else if (event instanceof YetAnotherEvent) {System.out.println("Handling YetAnotherEvent");}}// 带条件的事件监听@EventListener(condition = "#event.message.startsWith('important')")public void handleImportantEvents(MyCustomEvent event) {System.out.println("Received IMPORTANT event - " + event.getMessage());}
}
  1. 发布事件
    利用ApplicationContext发布事件,因为ApplicationContext有事件广播器
@Service
public class EventPublisherService implements ApplicationContextAware{private ConfigurableApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) {this.applicationContext = applicationContext;}public void publishEvent(String message) {MyCustomEvent event = new MyCustomEvent(message);applicationContext.publishEvent(event);}
}

二、整体流程

回顾一下spring的启动流程

  1. 在构造方法中,初始化了6个重要的BeanDefinition,其中有2个是EventListenerMethodProcessor和DefaultEventListenerFactory,之前介绍过这两个与@EventListener注解有关。
  2. 在refresh方法中,registerBeanPostProcessors这一步注册了ApplicationListenerDetector(检测bean是否为监听器类型),initApplicationEventMulticaster这一步初始化了SimpleApplicationEventMulticaster(事件广播器)
  3. 在refresh方法中,registerListeners这一步将所有的监听器注册到事件广播器中

我们知道了spring事件监听的相关组件,再来看看每个组件的具体功能

三、EventListenerMethodProcessor和DefaultEventListenerFactory

1.EventListenerMethodProcessor

EventListenerMethodProcessor实现了两个重要的接口BeanFactoryPostProcessor 和SmartInitializingSingleton

public class EventListenerMethodProcessorimplements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {}

1)BeanFactoryPostProcessor 是在容器执行refresh方法中的invokeBeanFactoryPostProcessors方法时调用,前面文章已经介绍过。看下代码

@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {this.beanFactory = beanFactory;Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);List<EventListenerFactory> factories = new ArrayList<>(beans.values());AnnotationAwareOrderComparator.sort(factories);this.eventListenerFactories = factories;}

就是在容器中找到类型为EventListenerFactory的bean,然后设置到eventListenerFactories 这个属性中,而容器中正好在构造阶段注册了DefaultEventListenerFactory这个BeanDefinition。注意,eventListenerFactories 是一个集合,也就是说我们也可以自己注册类型为EventListenerFactory的bean。当然我们现在只分析DefaultEventListenerFactory即可。

2)SmartInitializingSingleton是在所有的bean实例化初始化都完成后执行的,前面文章也介绍过。看下这里的实现代码:

@Overridepublic void afterSingletonsInstantiated() {ConfigurableListableBeanFactory beanFactory = this.beanFactory;Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");String[] beanNames = beanFactory.getBeanNamesForType(Object.class);for (String beanName : beanNames) {if (!ScopedProxyUtils.isScopedTarget(beanName)) {Class<?> type = null;try {//如果这个BeanDefinition是代理对象,使用被代理的class。因为被代理的class才有@EventListener注解type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);}catch (Throwable ex) {...}if (type != null) {...try {processBean(beanName, type);}catch (Throwable ex) {...}}}}}

可以看到就是遍历所有的beanName,调用processBean方法

private void processBean(final String beanName, final Class<?> targetType) {if (!this.nonAnnotatedClasses.contains(targetType) &&AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&!isSpringContainerClass(targetType)) {Map<Method, EventListener> annotatedMethods = null;try {//查找Class上所有被@EventListener注解的方法annotatedMethods = MethodIntrospector.selectMethods(targetType,(MethodIntrospector.MetadataLookup<EventListener>) method ->AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));}catch (Throwable ex) {。。。}}if (CollectionUtils.isEmpty(annotatedMethods)) {this.nonAnnotatedClasses.add(targetType);。。。}else {// Non-empty set of methodsConfigurableApplicationContext context = this.applicationContext;Assert.state(context != null, "No ApplicationContext set");List<EventListenerFactory> factories = this.eventListenerFactories;Assert.state(factories != null, "EventListenerFactory List not initialized");for (Method method : annotatedMethods.keySet()) {for (EventListenerFactory factory : factories) {if (factory.supportsMethod(method)) {//判断工厂类是否支持该方法Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));//利用工厂生成一个ApplicationListener实例ApplicationListener<?> applicationListener =factory.createApplicationListener(beanName, targetType, methodToUse);if (applicationListener instanceof ApplicationListenerMethodAdapter) {((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);}//将生成的ApplicationListener实例添加到Spring的applicationListeners中,后续会注册到事件广播器上context.addApplicationListener(applicationListener);break;}}}。。。}}}

逻辑如下:
a. 查找Class上所有被@EventListener注解的方法
b. 如果有被@EventListener注解的方法,遍历所有的EventListenerFactory,找到能处理这个方法的EventListenerFactory (适配器模式),利用EventListenerFactory工厂将该方法生成一个ApplicationListener实例。

可以通过@EventListener注解的属性增加一些功能,比如前面例子中的通过condition属性判断是不是要处理这个事件,这里就不展开了

c. 将生成的ApplicationListener实例添加到Spring的applicationListeners中,后续会注册到事件广播器上

2.DefaultEventListenerFactory

上面已经介绍了,这个类就是将被@EventListener注解的方法生成一个ApplicationListener实例。不展开了

3.ApplicationListenerDetector

实现了MergedBeanDefinitionPostProcessor

class ApplicationListenerDetector implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor {
}

MergedBeanDefinitionPostProcessor 有2个扩展点,一个是在createBeanInstance之后执行postProcessMergedBeanDefinition方法,这里就是判断是否为单例bean

public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {if (ApplicationListener.class.isAssignableFrom(beanType)) {this.singletonNames.put(beanName, beanDefinition.isSingleton());}}

另一个是在初始化后执行postProcessAfterInitialization方法:就是判断如果这个bean实现了ApplicationListener接口,且是单例bean,添加到Spring的applicationListeners中,后续会注册到事件广播器上

public Object postProcessAfterInitialization(Object bean, String beanName) {if (bean instanceof ApplicationListener) {// potentially not detected as a listener by getBeanNamesForType retrievalBoolean flag = this.singletonNames.get(beanName);if (Boolean.TRUE.equals(flag)) {// singleton bean (top-level or inner): register on the fly//添加到Spring的applicationListeners中,后续会注册到事件广播器上this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);}else if (Boolean.FALSE.equals(flag)) {if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {// inner bean with other scope - can't reliably process eventslogger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +"but is not reachable for event multicasting by its containing ApplicationContext " +"because it does not have singleton scope. Only top-level listener beans are allowed " +"to be of non-singleton scope.");}this.singletonNames.remove(beanName);}}return bean;}

4.initApplicationEventMulticaster

1)从容器中找beanName为applicationEventMulticaster的bean,如果存在,将其设置为applicationEventMulticaster
2)如果不存在,创建一个默认的SimpleApplicationEventMulticaster,设置为applicationEventMulticaster

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);beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);if (logger.isTraceEnabled()) {logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");}}}

5.registerListeners

遍历applicationListeners中的ApplicationListener,注册到ApplicationEventMulticaster中,这里才算是真正注册上了

for (ApplicationListener<?> listener : getApplicationListeners()) {getApplicationEventMulticaster().addApplicationListener(listener);}

6.SimpleApplicationEventMulticaster

注册ApplicationListener,就是将ApplicationListener放到内部类defaultRetriever的applicationListeners属性中

public void addApplicationListener(ApplicationListener<?> listener) {synchronized (this.defaultRetriever) {// Explicitly remove target for a proxy, if registered already,// in order to avoid double invocations of the same listener.Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);if (singletonTarget instanceof ApplicationListener) {this.defaultRetriever.applicationListeners.remove(singletonTarget);}this.defaultRetriever.applicationListeners.add(listener);this.retrieverCache.clear();}}

四、事件发布过程

上面已经介绍了监听器的注册,接下来看看事件发布过程

前面的例子中,我们是通过ApplicationContext的publishEvent方法来发布事件的

有2个方法,分别用来发布ApplicationEvent类型的事件和非ApplicationEvent 类型的事件

//发布ApplicationEvent类型的事件
public void publishEvent(ApplicationEvent event) {publishEvent(event, null);}
//发布非ApplicationEvent 类型的事件
public void publishEvent(Object event) {publishEvent(event, null);}

我们主要看发布ApplicationEvent类型的事件,通过getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);调用SimpleApplicationEventMulticaster的multicastEvent方法

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)) {//如果执行器不为空,用执行器调用监听器的onApplicationEvent方法if (executor != null) {executor.execute(() -> invokeListener(listener, event));}else {//如果执行器为空,直接调用监听器的onApplicationEvent方法invokeListener(listener, event);}}}

1)找到能处理该事件的监听器,遍历监听器
2)如果执行器不为空,用执行器调用监听器的onApplicationEvent方法。
这里是为了使用线程池来异步执行,而SimpleApplicationEventMulticaster的executor是为空的,那怎么异步呢?前面讲initApplicationEventMulticaster时,如果容器没有广播器才创建默认的SimpleApplicationEventMulticaster,那我们自己定义一个广播器继承SimpleApplicationEventMulticaster,并且设置一个线程池,那不就可以异步了吗
3)如果执行器为空,直接调用监听器的onApplicationEvent方法
这种情况就同步执行

getApplicationListeners方法有点复杂,因为为了支持非ApplicationEvent 类型的事件,做了一些包装

这里只简单介绍一下非ApplicationEvent 类型的事件的用法

@Component
public class MyListeners {// 类型1:直接声明payload类型@EventListenerpublic void handleString(String payload) {// 能接收 publishEvent("hello")}// 类型2:声明PayloadApplicationEvent泛型@EventListenerpublic void handlePayloadEvent(PayloadApplicationEvent<String> event) {String payload = event.getPayload();// 能接收 publishEvent("hello")}
}

总结

  1. 监听器需要注册到ApplicationEventMulticaster后才能监听事件,有2中注册方式
    1)在所有的bean实例化初始化都完成后,通过EventListenerMethodProcessor查找@EventListener注解的方法,将其生成ApplicationLister对象,然后注册到ApplicationEventMulticaster中
    2)通过ApplicationListenerDetector这个BPP,在bean的生命周期中执行初始化后方法postProcessAfterInitialization,如果这个bean实现了ApplicationListener接口,且是单例bean,注册到ApplicationEventMulticaster中
  2. 发布事件时,找到能处理该事件的所有监听器,遍历监听器,逐个执行监听器的onApplicationEvent方法。如果要异步执行,必须给广播器设置线程池。
  3. 事件分为ApplicationEvent类型的事件和非ApplicationEvent 类型的事件,都需要对应的监听器才能处理

相关文章:

  • 《会计研究》顶刊数据复刻!上市公司环境规制压力数据及研究价值
  • Android多线程下载文件拆解:原理、实现与优化
  • 2570. 合并两个二维数组 - 求和法
  • 每日leetcode
  • 手搓四人麻将程序
  • 如何应对kaggle离线安装环境?
  • 5月21日星期三今日早报简报微语报早读
  • Cross-Mix Monitoring for Medical Image Segmentation With Limited Supervision
  • 【C语言】复习~数组和指针
  • 云DNS智能解析:实现多区域部署
  • SpringBoot JAR 启动原理
  • 【Linux高级全栈开发】2.2.1 Linux服务器百万并发实现2.2.2 Posix API与网络协议栈
  • Mysql差异备份与恢复
  • 小黑黑prompt表述短语积累1
  • YOLO训练输入尺寸代表什么 --input_width 和 --input_height 参数
  • QGIS3.40.X使用OSM获取数据
  • 实践大模型提示工程(Prompt Engineering)
  • 民锋视角下的多因子金融分析模型实践
  • 电商项目-商品微服务-规格参数管理,分类与品牌管理需求分析
  • Spring AOP拦截失败
  • 网站开发中如何制作登录页面/黄金网站app大全
  • 广州美容网站建设/重庆seo网站运营
  • 新网站做seo优化步骤/百度推广电话销售好做吗
  • 网站被入侵后需做的检测 1/前端开发培训机构推荐
  • 丹灶做网站/seopc流量排名官网