Spring中ApplicationListener -【Spring底层原理】

淡淡的烟草味﹌ 2021-09-21 21:36 431阅读 0赞

目录

一、概述

二、实例分析

三、源码分析

四、总结


一、概述

在前面讲到了bean的拓展,这篇文章将讲述事件的拓展

ApplicationListener 是用来监听容器中发生的事件,只要事件发生,就会触发监听器的回调,完成事件驱动模型的开发

这里通过两个问题来进行概述,什么是事件?spring是如何实现的?

【1】什么是事件

事件是可以被控件识别的操作,如按下确定按钮,选择某个单选按钮或者复选框。每一种控件有自己可以识别的事件,如窗体的加载、单击、双击等事件,编辑框(文本框)的文本改变事,等等。事件有系统事件和用户事件。系统事件由系统激发,如时间每隔24小时,银行储户的存款日期增加一天。用户事件由用户激发,如用户点击按钮,在文本框中显示特定的文本。事件驱动控件执行某项功能。

【2】如何实现

Spring对事件机制也提供了支持,一个事件被发布后,被对应的监听器监听到,执行对应方法。并且Spring内已经提供了许多事件,ApplicationEvent可以说是Spring事件的顶级父类。ApplicationListener 是监听器的顶级接口,事件被触发后,onApplicationEvent方法就会执行

如果我们要写监听器,就要写这个监听器接口的实现类,ApplicationEvent泛型就是我们要监听的类,所以我们要监听或者是发布,都是ApplicationEvent及其下面的子事件,通过查看ApplicationEvent类,我们发现有以下子事件:

  1. ContextClosedEvent:关闭容器发布这个事件
  2. ContextRefreshedEvent:容器刷新完成发布这个事件(所有bean都进行了实例化,完成了创建)
  3. ContextStoppedEvent:容器停止时发布这个事件
  4. ContextStartedEvent:容器开始执行时发布这个事件

实现步骤:

  1. 写一个监听器(ApplicationListener 实现类)来监听某个事件(ApplicationEvent及其子类)
  2. 把监听器加到容器中
  3. 只要容器中有相关事件的发布,我们就能监听到这个事件,监听的便是上面说到的子事件
  4. 自定义发布一个事件:applicationContext.publishEvent()

二、实例分析

  1. // 启动测试类
  2. @Test
  3. public void TestMain(){
  4. AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
  5. // 自己发布一个事件
  6. applicationContext.publishEvent(new ApplicationEvent("自己发布的事件") {
  7. });
  8. applicationContext.close();
  9. }
  10. // ApplicationListener实现类
  11. @Component
  12. public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
  13. // 当容器中发布此事件后,该方法会触发
  14. public void onApplicationEvent(ApplicationEvent applicationEvent) {
  15. System.out.println("收到的事件:" + applicationEvent);
  16. }
  17. }
  18. // 配置类
  19. @Configuration
  20. @ComponentScan("listener")
  21. public class AppConfig {
  22. }

运行启动类,输出结果如下,以下三点说一下:

  • 容器启动时,会执行容器刷新完成事件,也就是ContextRefreshedEvent
  • 容器关闭时,会执行容器关闭事件,也就是ContextClosedEvent
  • 在启动类中,通过publishEvent来发布事件,执行这个方法的时候,ApplicationListener 就能监听到这个事件,就会回调onApplicationEvent执行

image-20210320174351733

三、源码分析

在上面的案例中,收到了三个事件,分别是:ContextRefreshedEventContextClosedEvent以及自己定义的MainTest$1[source=自己发布的事件],这几个事件在底层是如何收到的呢?,我们就通过源码来进行分析,在回掉方法onApplicationEvent打上断点,通过Debug查看源码:

【1】事件发布

通过Debug,我们可以看到,最先收到ContextRefreshedEvent事件,下面咱们就根据方法调用栈分析ContextRefreshedEvent如何发布的

image-20210320201912672

容器创建对象,调用refresh()方法——>finishRefresh()方法——>publishEvent()方法,调用getApplicationEventMulticaster()方法获取事件的多播器,也就是派发器

  1. protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
  2. Assert.notNull(event, "Event must not be null");
  3. Object applicationEvent;
  4. if (event instanceof ApplicationEvent) {
  5. applicationEvent = (ApplicationEvent)event;
  6. } else {
  7. applicationEvent = new PayloadApplicationEvent(this, event);
  8. if (eventType == null) {
  9. eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
  10. }
  11. }
  12. if (this.earlyApplicationEvents != null) {
  13. this.earlyApplicationEvents.add(applicationEvent);
  14. } else {
  15. // 获取事件的多播器,也就是派发器
  16. this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
  17. }
  18. if (this.parent != null) {
  19. if (this.parent instanceof AbstractApplicationContext) {
  20. ((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
  21. } else {
  22. this.parent.publishEvent(event);
  23. }
  24. }
  25. }

调用multicastEvent进行事件的派发

  • 获取所有ApplicationListener进行遍历
  • 判断是否可以用executor异步执行,可以的话使用executor进行异步派发,派发的时候我们可以自定义是同步还是异步
  • 否则同步执行派发

    public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {

    1. ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
    2. Executor executor = this.getTaskExecutor();
    3. Iterator var5 = this.getApplicationListeners(event, type).iterator();
    4. while(var5.hasNext()) {
    5. // 获取ApplicationListener进行遍历
    6. ApplicationListener<?> listener = (ApplicationListener)var5.next();
    7. // 判断是否可以用executor异步执行,可以的话使用executor进行异步派发
    8. if (executor != null) {
    9. executor.execute(() -> {
    10. this.invokeListener(listener, event);
    11. });
    12. } else {
    13. // 否则同步执行
    14. this.invokeListener(listener, event);
    15. }
    16. }

    }

执行invokeListener方法,拿到listener回调onApplicationEvent方法

  1. private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
  2. try {
  3. // 执行invokeListener方法,拿到listener回调onApplicationEvent方法
  4. listener.onApplicationEvent(event);
  5. } catch (ClassCastException var6) {
  6. String msg = var6.getMessage();
  7. if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {
  8. throw var6;
  9. }
  10. Log logger = LogFactory.getLog(this.getClass());
  11. if (logger.isTraceEnabled()) {
  12. logger.trace("Non-matching event type for listener: " + listener, var6);
  13. }
  14. }
  15. }

【2】获取事件派发器getApplicationEventMulticaster

容器创建对象,调用refresh()方法——>initApplicationEventMulticaster()方法,初始化ApplicationEventMulticaster

  • 先从容器中找是否有ID为“applicationEventMulticaster”的组件
  • 有,则通过getBean的方式获取到该组件
  • 如果没有,则创建一个简单的ApplicationEventMulticaster
  • 将创建的注册到容器的单实例bean中,这样我们就可以在其他组件要派发事件,自动注入这个applicationEventMulticaster

    protected void initApplicationEventMulticaster() {

    1. ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
    2. // 从bean工厂中找是否有ID为“applicationEventMulticaster”的组件
    3. if (beanFactory.containsLocalBean("applicationEventMulticaster")) {
    4. // 获取到该组件
    5. this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);
    6. if (this.logger.isTraceEnabled()) {
    7. this.logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
    8. }
    9. } else {
    10. // 如果没有则自己创建一个简单的ApplicationEventMulticaster
    11. this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
    12. // 将创建的注册到容器的单实例bean中
    13. beanFactory.registerSingleton("applicationEventMulticaster", this.applicationEventMulticaster);
    14. if (this.logger.isTraceEnabled()) {
    15. this.logger.trace("No 'applicationEventMulticaster' bean, using [" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
    16. }
    17. }

    }

【3】容器中有哪些监听器

容器创建对象,调用refresh()方法——>registerListeners()方法,注册监听器

  • getApplicationListeners:获取所有Listener
  • 从容器中拿到所有ApplicationListener类型的Listener组件
  • 把组件添加到getApplicationEventMulticaster派发器中,注册到派发器中

    protected void registerListeners() {

    1. // 获取所有Listener
    2. Iterator var1 = this.getApplicationListeners().iterator();
    3. while(var1.hasNext()) {
    4. ApplicationListener<?> listener = (ApplicationListener)var1.next();
    5. this.getApplicationEventMulticaster().addApplicationListener(listener);
    6. }
    7. // 从容器中拿到所有ApplicationListener类型的Listener组件
    8. String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false);
    9. String[] var7 = listenerBeanNames;
    10. int var3 = listenerBeanNames.length;
    11. for(int var4 = 0; var4 < var3; ++var4) {
    12. String listenerBeanName = var7[var4];
    13. // 把组件添加到getApplicationEventMulticaster派发器中,注册到派发器中
    14. this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    15. }
    16. Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    17. this.earlyApplicationEvents = null;
    18. if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
    19. Iterator var9 = earlyEventsToProcess.iterator();
    20. while(var9.hasNext()) {
    21. ApplicationEvent earlyEvent = (ApplicationEvent)var9.next();
    22. this.getApplicationEventMulticaster().multicastEvent(earlyEvent);
    23. }
    24. }

    }

四、总结

事件发布流程:

  1. refresh():容器创建对象
  2. finishRefresh():容器完成刷新
  3. publishEvent(new ContextRefreshedEvent()):发布事件,将容器刷新完成事件作为参数执行

    1. getApplicationEventMulticaster():获取到事件的多播器(派发器),就是把事件发送到多个监听器让他们同时感知
    2. multicastEvent:派发事件
    3. 通过循环获取到所有的ApplicationListener并进行判断

      1. 如果有executor,可以支持使用executor进行异步派发
      2. 否则,同步的方式直接执行listener方法,拿到listener回调onApplicationEvent方法

发表评论

表情:
评论列表 (有 0 条评论,431人围观)

还没有评论,来说两句吧...

相关阅读