七.Spring之ApplicationListener事件监听、@EventListener
看看注释:由应用事件监听器实现的接口,基于观察者设计模式。
方法是处理应用事件。
/**由应用事件监听器实现的接口,基于观察者设计模式* Interface to be implemented by application event listeners.* Based on the standard {@code java.util.EventListener} interface* for the Observer design pattern.** <p>As of Spring 3.0, an ApplicationListener can generically declare the event type* that it is interested in. When registered with a Spring ApplicationContext, events* will be filtered accordingly, with the listener getting invoked for matching event* objects only.** @author Rod Johnson* @author Juergen Hoeller* @param <E> the specific ApplicationEvent subclass to listen to* @see org.springframework.context.event.ApplicationEventMulticaster*/
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {/*** Handle an application event.* @param event the event to respond to*/void onApplicationEvent(E event);}
一.ApplicationListener
1.演示案例
1)、写一个监听器(ApplicationListener实现类)来监听某个事件(ApplicationEvent及其子类)
@EventListener;
原理:使用EventListenerMethodProcessor处理器来解析方法上的@EventListener;
2)、把监听器加入到容器;
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {//当容器中发布此事件以后,方法触发@Overridepublic void onApplicationEvent(ApplicationEvent event) {// TODO Auto-generated method stubSystem.out.println("收到事件:"+event);}}
3)单元测试:发布一个事件: applicationContext.publishEvent();
@Testpublic void test01(){AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);//发布事件;applicationContext.publishEvent(new ApplicationEvent(new String("我发布的时间")) {});applicationContext.close();}
4)、只要容器中有相关事件(ApplicationEvent)的发布,我们就能监听到这个事件;
ContextRefreshedEvent(ApplicationEvent的子类):容器刷新完成(所有bean都完全创建)会发布这个事件;
ContextClosedEvent(ApplicationEvent的子类):关闭容器会发布这个事件;
以及监听到我们在单元测试,自定义发出的ApplicationEvent事件。
2.事件发布监听原理
在自定义监听器打断点
看执行链
容器的refresh方法:finishRefresh
执行链一个个看,publishEvent(new ContextRefreshedEvent(this)); 发布了一个ContextRefreshedEvent
说明容器刷新完成会发布ContextRefreshedEvent事件
ContextRefreshedEvent是ApplicationEvent子类。
publishEvent(Object event, ResolvableType eventType)来看看发布流程
分两步
- getApplicationEventMulticaster() 得到事件多播器
- multicastEvent(applicationEvent, eventType) 传播事件
multicastEvent方法中,得到listeners,然后遍历执行 invokeListener。
这里如果execuror不为空,会采用线程异步执行invokeListener。
invokeListener调用了doInvokeListener,最后直接调用listener的 onApplicationEvent方法
这次ApplicationEvent事件是容器finishRefresh时发布的。 将这个断点放过,下一个ApplicationEvent事件是单元测试自定义发布的事件,可以看到发布流程是一样的执行链。
再放过断点,最后还有一个ApplicationEvent事件,可以看到是关闭容器时发布的:
单元测试调用 applicationContext.close();
总结:
1)、ContextRefreshedEvent事件:
1)、容器创建对象:refresh();
2)、finishRefresh();容器刷新完成会发布ContextRefreshedEvent事件
2)、自己发布事件;
3)、容器关闭会发布ContextClosedEvent;
【事件发布流程】:
3)、publishEvent(new ContextRefreshedEvent(this));
1)、获取事件的多播器(派发器):getApplicationEventMulticaster()
2)、multicastEvent派发事件:
3)、获取到所有的ApplicationListener;
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
1)、如果有Executor,可以支持使用Executor进行异步派发;
Executor executor = getTaskExecutor();
2)、否则,同步的方式直接执行listener方法;invokeListener(listener, event);
拿到listener回调onApplicationEvent方法;
3.事件多播器
看看ApplicationEventMulticaster的由来,在容器的refresh方法中,有一步初始化容器事件多播器
判断工厂中是否缓存了 applicationEventMulticaster 或者 有它的beanDefinition,如果有从工厂中获取或创建,
如果没有则new 一个 SimpleApplicationEventMulticaster,并且注册到spring
4.容器中的Listener
在发布事件时,会得到事件对应的监听器,遍历调用监听方法。
那listener从何处来?只要listener注入了容器,就可以通过BeanFactory得到。
getApplicationListeners方法往进点,也可以看到从beanFactory中中获取。
在容器refresh方法中有一个registerListeners,注册监听器,进去看看
但看代码,并不是产生Listener的实例并注册到容器,而是得到Listener的bean名称,并让他们和多播器ApplicationEventMulticaster产生关联,把他们添加到多播器中。
那Listener是何时创建并注册到容器中呢?可以写一个BeanPostProcessor 来测试一下。
判断如果是Listener初始化时,就把beanName打印出来。
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean instanceof ApplicationListener)System.out.println("当前初始化的bean是"+beanName);return bean;}}
打断点,看看执行流程:
listener 是在refresh的finishBeanFactoryInitialization 创建的
补充:
在容器refresh的注册beanPostProcessor registerBeanPostProcessors方法时,最后一步是注册一个ApplicationListenerDetector。
ApplicationListenerDetector的初始化后置处理方法是判断bean是否实现 监听器接口,如果是,给ApplicatioContext的多播器添加上。
二.@EventListener
监听器有更方便的用法。
1.演示案例
写一个Userservice,在方法上标注@EventListener,属性是监听的事件Class。
如果监听到此事件Class,则会执行被注解的方法。
@Service
public class UserService {@EventListener(classes={ApplicationEvent.class})public void listen(ApplicationEvent event){System.out.println("UserService。。监听到的事件:"+event);}@EventListener(classes = {Tom.class, Jerry.class})public void tom(Object event){System.out.println("tom---------------"+event);if (event instanceof Tom){System.out.println("tom");}if (event instanceof Jerry)System.out.println("jerry");}
}
事件类Tom继承ApplicationEvent
public class Tom extends ApplicationEvent {/*** Create a new ApplicationEvent.** @param source the object on which the event initially occurred (never {@code null})*/public Tom(Object source) {super(source);}
}
事件类Jerry
public class Jerry {
}
单元测试
@Testpublic void test01(){AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);//发布事件;applicationContext.publishEvent(new ApplicationEvent(new String("我发布的时间")) {});applicationContext.publishEvent(new Jerry());applicationContext.publishEvent(new Tom(new String("hi tom")));applicationContext.close();}
applicationContext.publishEvent是个重载方法,参数是ApplicationEvent 和Object都可以
@Overridepublic void publishEvent(ApplicationEvent event) {publishEvent(event, null);}@Overridepublic void publishEvent(Object event) {publishEvent(event, null);}
发布的ApplicationEvent事件,只有监听ApplicationEvent.class的方法能收到。
发布的Tom事件,监听Tom 和 ApplicationEvent 的方法都能收到。
发布的Jerry事件,只有监听Jerry的方法能收到。
。
2.原理
EventListenr注释了EventListenerMethodProcessor
EventListenerMethodProcessor实现了SmartInitializingSingleton 接口
SmartInitializingSingleton接口
关键在SmartInitializingSingleton接口,看注释,当所有单实例创建完成后,调用afterSingletonsInstantiated方法。
如何保证容器中所有的单实例创建完成后,会执行实现SmartInitializingSingleton接口实例的afterSingletonsInstantiated方法?
答案在容器refresh方法的最后一步,finishBeanFactoryInitialization方法中的beanFactory.preInstantiateSingletons() (创建剩余的单实例);
先遍历beanNames,去创建实例,创建完成后又遍历beanNames,判断实例是否实现SmartInitializingSingleton接口,实现则执行实例的afterSingletonsInstantiated方法
回到EventListenerMethodProcessor
在EventListenerMethodProcessor的afterSingletonsInstantiated方法打断点
内容是遍历容器所有beanNames,因为我们在UserService类上加了@EventListener注解,所以把beanName遍历到userService看如何执行的。
通过beanName尝试找到 bean对应的Class, AutoProxyUtils.determineTargetClass(this.applicationContext.getBeanFactory(), beanName);
执行到processBean
this.nonAnnotatedClasses.contains(targetType) :判断targetType 代表的Class 是否标注了注解。
annotatedMethods = MethodIntrospector.selectMethods : 得到targetType 中被注解的方法, UserService类有Listen和tom方法被注解
如果当前beanName代表的类targetype上没有注解,就加入到nonAnnotatedClasses,图中遍历的是自定义的Blue类,没有注解。
每个带@EventListener方法就会创建一个ApplicationListener对象
接着上面,得到所有遍历注解的方法后, 遍历
factory.supportsMethod(method) :看 EventListenerFactory 是否支持这个方法
ApplicationListener<?> applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse)
如果支持就用 EventListenerFactory 创建一个 ApplicationListener ,如果创建出来是 ApplicationListenerMethodAdapter适配器,就再初始化一下,总之得到一个ApplicationListener
ApplicationListenerMethodAdapter是 ApplicationListener的子类。
this.applicationContext.addApplicationListener(applicationListener): 把创建的ApplicationListener加入到了 容器中!
如何加入容器? 其实是加到了多播器中 applicationEventMulticaster
总结下:方法如果注解了@EventListener,就会对用创建一个ApplicationListener 或者是ApplicationListenerAdpter加入到applicationEventMulticaster中。UserService有两个方法都标了注解,就创建了两个监听器。
发布事件如何找到对应的监听方法
虽然创建了监听器,之前也讲过可以通过 事件寻找到订阅它的监听器,但在发布事件时是如何调用到UserService的方法的,毕竟执行方法还是在UserService,并不是在ApplicationListener,而且看断点信息创建的ApplicationListener并不是一个代理对象拥有对应的UserService的方法。
把断点打到UseService的两个监听方法上
看执行流程,最前面几步还是 发布事件---从多播器找监听器---执行监听方法 onApplicationEvent
从onApplicationEvent方法开始不同,如果我们自定义实现 ApplicationListener 接口,则从这一步会直接执行我们自定义的方法。
但现在用的@EventListener注解,看断点目前执行到的是 ApplicationListenerMethodAdapter,一个监听适配器,在为监听注解方法创建 监听器时 创建的就是这个 监听适配器。
执行监听方法,ApplicationListenerMethodAdapter.onApplicationEvent 调用的是 processEvent(event);
重点来了,在 processEvent让断点进来。
Object[] args = resolveArguments(event):解析事件对象,得到事件的负载核和一些信息
shouldHandle判断是否应该处理。
doInvoke 通过事件信息得到了 事件对应的标注@EventListener 的userService实例.
原来是通过beanName寻找在容器中的 实例,因为创建ApplicationListener时保存了 对应方法类的信息。通过userService的beanName找到了userService实例。
this.bridgedMethod.invoke(bean, args);通过反射调用了 userService中的监听方法。
三.总结
- 自定义实现ApplicationListener接口的流程:发布事件时,通过此事件找到 多播器、找订阅它的监听器,然后执行监听方法。
- 如果用@EventListener注解,则发布事件时找到是监听适配器(ApplicationListenerAdpter),监听适配器是监听器的子类,在创建ApplicationListenerAdpter时,其中保存了注解@EventListene的bean的信息,然后通过监听适配器从容器中找到对应bean,再执行bean中的监听方法。
七.Spring之ApplicationListener事件监听、@EventListener相关推荐
- Spring容器的事件监听机制(简单明了的介绍)
文章目录 前言 事件 1. 定义事件 2. 定义监听器 3. 定义发布器 Spring容器的事件监听机制 1.事件的继承类图 监听器的继承类图 总结 前言 上一篇我们介绍了SpringFactorie ...
- Java 创建事件Event、事件监听EventListener、事件发布publishEvent
一.概述 个人认为,事件机制一般可由:事件源source,事件对象Event,事件监听EventListener,事件发布publishEvent组成 事件源:引起事件发生的源: User用户信息, ...
- spring中的事件监听机制
Spring event listener 介绍 example 简单原理解释 自定义事件.监听和发布 事件 监听器 发布者 测试 更加一般的事件 @EventListener原理 介绍 exampl ...
- spring容器启动事件监听
原文地址 关键字:spring容器加载完毕做一件事情(利用ContextRefreshedEvent事件) 应用场景: 很多时候我们想要在某个类加载完毕时干某件事情,但是使用了spring管理对象,我 ...
- Spring5源码 - 12 Spring事件监听机制_异步事件监听应用及源码解析
文章目录 Pre 实现原理 应用 配置类 Event事件 事件监听 EventListener 发布事件 publishEvent 源码解析 (反推) Spring默认的事件广播器 SimpleApp ...
- java 事件监听应用_Spring Boot应用事件监听示例详解
前言 本文主要给大家介绍了关于Spring Boot应用事件监听的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧 1. Spring Boot特有的应用事件 除了Spring ...
- js 监听 安卓事件_百行代码实现js事件监听实现跨页面数据传输
百行代码实现js事件监听实现跨页面数据传输 使用场景 类似消息队列的使用场景,支持同页面和跨页面通信,发送消息和接收消息 技术原理 跨页面通信: 基于事件监听,通过监听 storage事件监听回调机制 ...
- Spring5源码 - 13 Spring事件监听机制_@EventListener源码解析
文章目录 Pre 概览 开天辟地的时候初始化的处理器 @EventListener EventListenerMethodProcessor afterSingletonsInstantiated 小 ...
- 框架源码专题:Spring的事件监听、发布机制 ApplicationListener
文章目录 1.Spring内置事件 2.自定义事件 3.事件监听器 4.事件发布 publishEvent 4.Spring事件原理 5. 面试题:怎么样可以在所有Bean创建完后做扩展代码? 6. ...
最新文章
- day34 异常处理、断言、socket之ftp协议
- [原]JS ajax类的三种封装形式及简单对比
- Java中泛型的使用场景
- NDK Socket编程:面向连接的通信(tcp)
- 【蜕变之路】第29天 CAST和CONVERT的区别(2019年3月19日)
- RGB转YUV420
- Golang实践录:命令行cobra库实例优化
- 笔记本重新启动计算机,为什么笔记本电脑突然重新启动_计算机的基本知识_IT /计算机_信息...
- 这是你所了解的FaaS 么?——无服务计算的10个思考
- 互换性测量与技术——偏差与公差的计算,公差图的绘制,配合与公差等级的选择方法
- 基于Arduino的学习、记忆机械手
- yxy小蒟蒻的201111总结
- 中间件技术及双十一实践·EagleEye篇
- RIGHT-BICEP测试第二次
- Windows2000、2003浏览器无法上网、无法联网
- python安装hyperlpr
- 5G通信下FBMC-OQAM的误码率仿真
- 07JavaScript数组与字符串对象
- 云原生Istio安装和使用
- eclipse反编译离线安装