Spring中ApplicationListener -【Spring底层原理】

Spring框架底层原理 专栏收录该内容
17 篇文章 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()

二、实例分析

// 启动测试类
@Test
public void TestMain(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    // 自己发布一个事件
    applicationContext.publishEvent(new ApplicationEvent("自己发布的事件") {
    });
    applicationContext.close();
}

// ApplicationListener实现类
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
    // 当容器中发布此事件后,该方法会触发
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        System.out.println("收到的事件:" + applicationEvent);
    }
}

// 配置类
@Configuration
@ComponentScan("listener")
public class AppConfig {
}

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

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

image-20210320174351733

三、源码分析

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

【1】事件发布

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

image-20210320201912672

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

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");
    Object applicationEvent;
    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent)event;
    } else {
        applicationEvent = new PayloadApplicationEvent(this, event);
        if (eventType == null) {
            eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
        }
    }

    if (this.earlyApplicationEvents != null) {
        this.earlyApplicationEvents.add(applicationEvent);
    } else {
        // 获取事件的多播器,也就是派发器
        this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
    }

    if (this.parent != null) {
        if (this.parent instanceof AbstractApplicationContext) {
            ((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
        } else {
            this.parent.publishEvent(event);
        }
    }

}

调用multicastEvent进行事件的派发

  • 获取所有ApplicationListener进行遍历
  • 判断是否可以用executor异步执行,可以的话使用executor进行异步派发,派发的时候我们可以自定义是同步还是异步
  • 否则同步执行派发
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
    Executor executor = this.getTaskExecutor();
    Iterator var5 = this.getApplicationListeners(event, type).iterator();

    while(var5.hasNext()) {
        // 获取ApplicationListener进行遍历
        ApplicationListener<?> listener = (ApplicationListener)var5.next();
        // 判断是否可以用executor异步执行,可以的话使用executor进行异步派发
        if (executor != null) {
            executor.execute(() -> {
                this.invokeListener(listener, event);
            });
        } else {
            // 否则同步执行
            this.invokeListener(listener, event);
        }
    }
}

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

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
    try {
        // 执行invokeListener方法,拿到listener回调onApplicationEvent方法
        listener.onApplicationEvent(event);
    } catch (ClassCastException var6) {
        String msg = var6.getMessage();
        if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {
            throw var6;
        }

        Log logger = LogFactory.getLog(this.getClass());
        if (logger.isTraceEnabled()) {
            logger.trace("Non-matching event type for listener: " + listener, var6);
        }
    }
}

【2】获取事件派发器getApplicationEventMulticaster

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

  • 先从容器中找是否有ID为“applicationEventMulticaster”的组件
  • 有,则通过getBean的方式获取到该组件
  • 如果没有,则创建一个简单的ApplicationEventMulticaster
  • 将创建的注册到容器的单实例bean中,这样我们就可以在其他组件要派发事件,自动注入这个applicationEventMulticaster
protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
    // 从bean工厂中找是否有ID为“applicationEventMulticaster”的组件
    if (beanFactory.containsLocalBean("applicationEventMulticaster")) {
        // 获取到该组件
        this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
        }
    } else {
        // 如果没有则自己创建一个简单的ApplicationEventMulticaster
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        // 将创建的注册到容器的单实例bean中
        beanFactory.registerSingleton("applicationEventMulticaster", this.applicationEventMulticaster);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("No 'applicationEventMulticaster' bean, using [" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
        }
    }
}

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

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

  • getApplicationListeners:获取所有Listener
  • 从容器中拿到所有ApplicationListener类型的Listener组件
  • 把组件添加到getApplicationEventMulticaster派发器中,注册到派发器中
protected void registerListeners() {
    // 获取所有Listener
    Iterator var1 = this.getApplicationListeners().iterator();

    while(var1.hasNext()) {
        ApplicationListener<?> listener = (ApplicationListener)var1.next();
        this.getApplicationEventMulticaster().addApplicationListener(listener);
    }

    // 从容器中拿到所有ApplicationListener类型的Listener组件
    String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false);
    String[] var7 = listenerBeanNames;
    int var3 = listenerBeanNames.length;

    for(int var4 = 0; var4 < var3; ++var4) {
        String listenerBeanName = var7[var4];
        // 把组件添加到getApplicationEventMulticaster派发器中,注册到派发器中
        this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }

    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;
    if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
        Iterator var9 = earlyEventsToProcess.iterator();

        while(var9.hasNext()) {
            ApplicationEvent earlyEvent = (ApplicationEvent)var9.next();
            this.getApplicationEventMulticaster().multicastEvent(earlyEvent);
        }
    }

}

四、总结

事件发布流程:

  1. refresh():容器创建对象
  2. finishRefresh():容器完成刷新
  3. publishEvent(new ContextRefreshedEvent()):发布事件,将容器刷新完成事件作为参数执行
    1. getApplicationEventMulticaster():获取到事件的多播器(派发器),就是把事件发送到多个监听器让他们同时感知
    2. multicastEvent:派发事件
    3. 通过循环获取到所有的ApplicationListener并进行判断
      1. 如果有executor,可以支持使用executor进行异步派发
      2. 否则,同步的方式直接执行listener方法,拿到listener回调onApplicationEvent方法
  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值