看看注释:由应用事件监听器实现的接口,基于观察者设计模式。

方法是处理应用事件。

/**由应用事件监听器实现的接口,基于观察者设计模式* 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相关推荐

  1. Spring容器的事件监听机制(简单明了的介绍)

    文章目录 前言 事件 1. 定义事件 2. 定义监听器 3. 定义发布器 Spring容器的事件监听机制 1.事件的继承类图 监听器的继承类图 总结 前言 上一篇我们介绍了SpringFactorie ...

  2. Java 创建事件Event、事件监听EventListener、事件发布publishEvent

    一.概述 个人认为,事件机制一般可由:事件源source,事件对象Event,事件监听EventListener,事件发布publishEvent组成 事件源:引起事件发生的源: User用户信息, ...

  3. spring中的事件监听机制

    Spring event listener 介绍 example 简单原理解释 自定义事件.监听和发布 事件 监听器 发布者 测试 更加一般的事件 @EventListener原理 介绍 exampl ...

  4. spring容器启动事件监听

    原文地址 关键字:spring容器加载完毕做一件事情(利用ContextRefreshedEvent事件) 应用场景: 很多时候我们想要在某个类加载完毕时干某件事情,但是使用了spring管理对象,我 ...

  5. Spring5源码 - 12 Spring事件监听机制_异步事件监听应用及源码解析

    文章目录 Pre 实现原理 应用 配置类 Event事件 事件监听 EventListener 发布事件 publishEvent 源码解析 (反推) Spring默认的事件广播器 SimpleApp ...

  6. java 事件监听应用_Spring Boot应用事件监听示例详解

    前言 本文主要给大家介绍了关于Spring Boot应用事件监听的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧 1. Spring Boot特有的应用事件 除了Spring ...

  7. js 监听 安卓事件_百行代码实现js事件监听实现跨页面数据传输

    百行代码实现js事件监听实现跨页面数据传输 使用场景 类似消息队列的使用场景,支持同页面和跨页面通信,发送消息和接收消息 技术原理 跨页面通信: 基于事件监听,通过监听 storage事件监听回调机制 ...

  8. Spring5源码 - 13 Spring事件监听机制_@EventListener源码解析

    文章目录 Pre 概览 开天辟地的时候初始化的处理器 @EventListener EventListenerMethodProcessor afterSingletonsInstantiated 小 ...

  9. 框架源码专题:Spring的事件监听、发布机制 ApplicationListener

    文章目录 1.Spring内置事件 2.自定义事件 3.事件监听器 4.事件发布 publishEvent 4.Spring事件原理 5. 面试题:怎么样可以在所有Bean创建完后做扩展代码? 6. ...

最新文章

  1. day34 异常处理、断言、socket之ftp协议
  2. [原]JS ajax类的三种封装形式及简单对比
  3. Java中泛型的使用场景
  4. NDK Socket编程:面向连接的通信(tcp)
  5. 【蜕变之路】第29天 CAST和CONVERT的区别(2019年3月19日)
  6. RGB转YUV420
  7. Golang实践录:命令行cobra库实例优化
  8. 笔记本重新启动计算机,为什么笔记本电脑突然重新启动_计算机的基本知识_IT /计算机_信息...
  9. 这是你所了解的FaaS 么?——无服务计算的10个思考
  10. 互换性测量与技术——偏差与公差的计算,公差图的绘制,配合与公差等级的选择方法
  11. 基于Arduino的学习、记忆机械手
  12. yxy小蒟蒻的201111总结
  13. 中间件技术及双十一实践·EagleEye篇
  14. RIGHT-BICEP测试第二次
  15. Windows2000、2003浏览器无法上网、无法联网
  16. python安装hyperlpr
  17. 5G通信下FBMC-OQAM的误码率仿真
  18. 07JavaScript数组与字符串对象
  19. 云原生Istio安装和使用
  20. eclipse反编译离线安装

热门文章

  1. 2022春招、金三银四,面试官必问的1000道Java面试题及答案整理
  2. SpringBoot整合第三方技术
  3. #14# SCCM管理 - 维护窗口
  4. 雅思作文模板.html,雅思G类小作文万能模板参考
  5. 4、mutations
  6. idea启动多个相同的项目
  7. 将 JSON 文件转换为表格格式
  8. 多商户商城系统功能拆解38讲-平台端营销-砍价商品
  9. Hexo博客更换电脑的解决办法
  10. 更强大的二维码识别工具zBar