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

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


相关文章
|
2月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
87 2
|
19天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
69 14
|
1月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
54 2
|
1月前
|
缓存 Java 数据库连接
深入探讨:Spring与MyBatis中的连接池与缓存机制
Spring 与 MyBatis 提供了强大的连接池和缓存机制,通过合理配置和使用这些机制,可以显著提升应用的性能和可扩展性。连接池通过复用数据库连接减少了连接创建和销毁的开销,而 MyBatis 的一级缓存和二级缓存则通过缓存查询结果减少了数据库访问次数。在实际应用中,结合具体的业务需求和系统架构,优化连接池和缓存的配置,是提升系统性能的重要手段。
59 4
|
2月前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
77 8
|
8月前
|
安全 Java 应用服务中间件
阿里技术官架构使用总结:Spring+MyBatis源码+Tomcat架构解析等
分享Java技术文以及学习经验也有一段时间了,实际上作为程序员,我们都清楚学习的重要性,毕竟时代在发展,互联网之下,稍有一些落后可能就会被淘汰掉,因此我们需要不断去审视自己,通过学习来让自己得到相应的提升。
|
8月前
|
Java 关系型数据库 数据库连接
Spring源码解析--深入Spring事务原理
本文将带领大家领略Spring事务的风采,Spring事务是我们在日常开发中经常会遇到的,也是各种大小面试中的高频题,希望通过本文,能让大家对Spring事务有个深入的了解,无论开发还是面试,都不会让Spring事务成为拦路虎。
108 1
|
3月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
194 5
|
3月前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
3月前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
152 9