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

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 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


相关文章
|
14天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
45 2
|
4天前
|
编译器 PHP 开发者
PHP 8新特性解析与实战应用####
随着PHP 8的发布,这一经典编程语言迎来了诸多令人瞩目的新特性和性能优化。本文将深入探讨PHP 8中的几个关键新功能,包括命名参数、JIT编译器、新的字符串处理函数以及错误处理改进等。通过实际代码示例,展示如何在现有项目中有效利用这些新特性来提升代码的可读性、维护性和执行效率。无论你是PHP新手还是经验丰富的开发者,本文都将为你提供实用的技术洞察和最佳实践指导。 ####
15 1
|
10天前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
15天前
RS-485网络中的标准端接与交流电端接应用解析
RS-485,作为一种广泛应用的差分信号传输标准,因其传输距离远、抗干扰能力强、支持多点通讯等优点,在工业自动化、智能建筑、交通运输等领域得到了广泛应用。在构建RS-485网络时,端接技术扮演着至关重要的角色,它直接影响到网络的信号完整性、稳定性和通信质量。
|
5天前
|
存储 供应链 算法
深入解析区块链技术的核心原理与应用前景
深入解析区块链技术的核心原理与应用前景
20 0
|
6天前
|
存储 监控 API
深入解析微服务架构及其在现代应用中的实践
深入解析微服务架构及其在现代应用中的实践
18 0
|
2月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
70 0
|
2月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
57 0
|
2月前
|
存储 Java C++
Collection-PriorityQueue源码解析
Collection-PriorityQueue源码解析
62 0
|
2月前
|
安全 Java 程序员
Collection-Stack&Queue源码解析
Collection-Stack&Queue源码解析
84 0