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

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

20200914143739548.png

Pre

Spring5源码 - 11 Spring事件监听机制_源码篇


实现原理


Spring提供的事件机制,默认是同步的。如果想要使用异步事件监听,可以自己实现ApplicationEventMulticaster接口,并在Spring容器中注册id为applicationEventMulticaster的Bean , 设置 executor 。


Spring会遍历所有的ApplicationListener, 如果 taskExecutor 不为空,这开启异步线程执行。


20201030211712324.png


应用

配置类

package com.artisan.eventlistener2;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
@Configuration
@ComponentScan("com.artisan.eventlistener2")
public class ArtisanConfig {
  @Bean(name = "applicationEventMulticaster") // Step1: id必须叫 applicationEventMulticaster
  public ApplicationEventMulticaster multicaster(){
     // Step2:实例化SimpleApplicationEventMulticaster 
    SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
    // Step3:设置TaskExecutor 
    simpleApplicationEventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
    return simpleApplicationEventMulticaster ;
  }
}


Event事件

package com.artisan.eventlistener2;
import org.springframework.context.ApplicationEvent;
public class ArtisanEvent  extends ApplicationEvent {
  private String msg ;
  public ArtisanEvent(Object source) {
    super(source);
  }
  public ArtisanEvent(Object source,String msg) {
    super(source);
    this.msg = msg ;
  }
  public void print(){
    System.out.println(Thread.currentThread().getName() +  "-----" + msg);
  }
}


事件监听 EventListener

package com.artisan.eventlistener2;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class ArtisanListenerByAnno   {
  @EventListener(ArtisanEvent.class)
  public void onApplicationEvent(ArtisanEvent event) {
    System.out.println(Thread.currentThread().getName() +  " EventListener  监听到ArtisanEvent.....");
    event.print();
  }
}


发布事件 publishEvent

package com.artisan.eventlistener2;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class ArtisanTest {
  public static void main(String[] args) {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ArtisanConfig.class);
    // 模拟发布事件
    ac.publishEvent(new ArtisanEvent("xxxx","msg from artisanEvent"));
    System.out.println(Thread.currentThread().getName() + "  over");
  }
}

【结果】


20201030213447353.png


如果我们把配置类中的

@Bean(name = "applicationEventMulticaster")
  public ApplicationEventMulticaster multicaster(){
    SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
    simpleApplicationEventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
    return simpleApplicationEventMulticaster ;
  }


移除掉,重新运行

20201030215134632.png


就变成了默认的同步监听了。。。。


源码解析 (反推)

Spring默认的事件广播器 SimpleApplicationEventMulticaster#multicastEvent

我们分析一下 ac.publishEvent(new ArtisanEvent("xxxx","msg from artisanEvent")); 最终会调用到

org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)


20201031002954784.png


获取executor

executor不为null则异步处理, 那看下executor = getTaskExecutor();


20201031003051233.png


SimpleApplicationEventMulticaster的一个属性


20201031003111294.png

20201031003145937.png


那只要在实例化SimpleApplicationEventMulticaster的时候 set属性值就可以了哇。

所以现在的问题是什么时候给线程池属性赋值的问题?


设置executor

我们知道

org.springframework.context.support.AbstractApplicationContext#refresh


20201031003454677.png


看看 initApplicationEventMulticaster

protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    //判断IOC容器中包含applicationEventMulticaster 事件多播器的Bean的name
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
        //创建一个applicationEventMulticaster的bean放在IOC 容器中,bean的name 为applicationEventMulticaster
      this.applicationEventMulticaster =beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
      if (logger.isDebugEnabled()) {
        logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
      }
    }
    else { //容器中不包含一个beanName 为applicationEventMulticaster的多播器组件
        //创建一个SimpleApplicationEventMulticaster 多播器
      this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
      //注册到容器中
      beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
      if (logger.isDebugEnabled()) {
        logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
            APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
            "': using default [" + this.applicationEventMulticaster + "]");
      }
    }
  }


通过源码我们知道,Spring会先从容器中找 bean name 为 “applicationEventMulticaster” 的 bean,so问题就简单了,我们只要自定义个 bean name 为 applicationEventMulticaster 的 bean,并给其属性 taskExecutor 赋上自定义的线程池即可,这个时候就能实现异步事件处理了 .


得出操作步骤

所以,可以这么配置

方式一

@Configuration
@ComponentScan("com.artisan.eventlistener2")
public class ArtisanConfig {
  @Bean(name = "applicationEventMulticaster")
  public ApplicationEventMulticaster multicaster(){
    SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
    simpleApplicationEventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
    return simpleApplicationEventMulticaster ;
  }
}


方式二

或者写一个类实现 AbstractApplicationEventMulticaster ,仿照 SimpleApplicationEventMulticaster 即可

package com.artisan.eventlistener2;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.AbstractApplicationEventMulticaster;
import org.springframework.core.ResolvableType;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import java.util.Iterator;
@Component("applicationEventMulticaster")
public class AsyncApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
  private TaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
  public void setTaskExecutor(TaskExecutor taskExecutor) {
    this.taskExecutor = (taskExecutor != null ? taskExecutor : new SimpleAsyncTaskExecutor());
  }
  protected TaskExecutor getTaskExecutor() {
    return this.taskExecutor;
  }
  @Override
  @SuppressWarnings("unchecked")
  public void multicastEvent(final ApplicationEvent event) {
  }
  @Override
  public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
    for (Iterator<ApplicationListener<?>> it = getApplicationListeners().iterator(); it.hasNext();) {
      final ApplicationListener listener =  it.next();
      System.out.println("-----------自定义异步事件监听-----------");
      getTaskExecutor().execute(() -> listener.onApplicationEvent(event));
    }
  }
}


20201031104108279.png



其他开启异步的方式

@EnbaleAsyn + @Async


相关文章
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
693 70
|
9月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
1245 0
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
514 2
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
1334 29
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
539 4
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
移动开发 前端开发 JavaScript
从入门到精通:H5游戏源码开发技术全解析与未来趋势洞察
H5游戏凭借其跨平台、易传播和开发成本低的优势,近年来发展迅猛。接下来,让我们深入了解 H5 游戏源码开发的技术教程以及未来的发展趋势。
|
存储 前端开发 JavaScript
在线教育网课系统源码开发指南:功能设计与技术实现深度解析
在线教育网课系统是近年来发展迅猛的教育形式的核心载体,具备用户管理、课程管理、教学互动、学习评估等功能。本文从功能和技术两方面解析其源码开发,涵盖前端(HTML5、CSS3、JavaScript等)、后端(Java、Python等)、流媒体及云计算技术,并强调安全性、稳定性和用户体验的重要性。
|
机器学习/深度学习 自然语言处理 算法
生成式 AI 大语言模型(LLMs)核心算法及源码解析:预训练篇
生成式 AI 大语言模型(LLMs)核心算法及源码解析:预训练篇
3897 1

推荐镜像

更多
  • DNS