Spring事件如何异步执行?

开发 前端
从Spring 4.2开始,事件基础设施得到了显著改进,并提供了基于注释的模型以及发布任意事件的能力(也就是说,不一定是从ApplicationEvent扩展的对象)。当这样的对象被发布时,我们将它包装在一个事件中。

环境:SpringBoot2.7.16

1. 简介

ApplicationContext 中的事件处理是通过 ApplicationEvent 类和 ApplicationListener 接口提供的。如果在上下文中部署了实现 ApplicationListener 接口的 Bean,那么每次 ApplicationEvent 发布到 ApplicationContext 时,都会通知该 Bean。从本质上讲,这就是标准的观察者设计模式。

从Spring 4.2开始,事件基础设施得到了显著改进,并提供了基于注释的模型以及发布任意事件的能力(也就是说,不一定是从ApplicationEvent扩展的对象)。当这样的对象被发布时,我们将它包装在一个事件中。

以下是一个简单的事件应用

1.1 定义事件对象

public class PackEvent extends ApplicationEvent {


  private static final long serialVersionUID = 1L;


  public PackEvent(Object source) {
    super(source);
  }
}

1.2 定义事件监听

@Component
public class PackEventListener implements ApplicationListener<PackEvent> {


  @Override
  public void onApplicationEvent(PackEvent event) {
    System.out.println("触发事件...") ;
  }
}

1.3 发布事件

@Resource
private ApplicationEventMulticaster eventMulticaster ;
public void run(ApplicationArguments args) throws Exception {
  eventMulticaster.multicastEvent(new PackEvent("自定义Pack")) ;
}

以上Spring事件系统的完整应用实例。在默认情况下该种事件处理方式是同步的,也就是事件的发布者与事件的处理都是同一个线程中,那这就要求我们的事件处理程序不应该处理复杂耗时的任务,否则会影响我们的主业务系统。那如何异步处理事件呢?

2. 事件异步处理

2.1 通过@Async注解

该种方式是最简单的方式了,开启异步功能,在基于注解的事件监听方法上使用@Async注解。

开启异步任务功能更

@EnableAsync
public class AppApplication {}

基于注解事件监听

@Async
@EventListener({PackEvent.class})
public void packEventListener(PackEvent event) {
  System.out.printf("%s - 事件发生了...%s%n", Thread.currentThread().getName(), event.getSource()) ;
}

执行结果

task-1 - 事件发生了...自定义Pack

线程名已经变为了task-1。task-前缀是异步线程的默认名。关于异步任务执行应用的线程池配置,查看下面这篇文章。

Spring任务调度&异步任务&Web异步请求三者如何配置线程池?

上面是基于注解的方式应用异步执行事件处理。对于在简介中通过实现ApplicationListener接口的方式又该如何处理呢?

对于这种方式,我们可以通过两种方式进行处理:

2.2 自定义线程池

  • 在事件监听处理程序中开启异步线程
@Component
public class PackEventListener implements ApplicationListener<PackEvent> {


  @Override
  public void onApplicationEvent(PackEvent event) {
    new Thread(() -> {
      System.out.printf("%s触发事件...%n", Thread.currentThread().getName()) ;
    }).start() ;
  }
}
  • 自定义事件广播器
@Bean
TaskExecutor taskExecutor() {
  ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor() ;
  taskExecutor.setThreadNamePrefix("pack-event-") ;
  taskExecutor.setCorePoolSize(5) ;
  taskExecutor.setQueueCapacity(100) ;
  taskExecutor.setMaxPoolSize(5) ;
  taskExecutor.initialize() ;
  return taskExecutor ;
}


// 注意beanName必须为applicationEventMulticaster;下面的源码中你将看到
@Bean(name  = AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
SimpleApplicationEventMulticaster eventMulticaster(BeanFactory beanFactory) {
  SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster(beanFactory) ;
  eventMulticaster.setTaskExecutor(taskExecutor()) ;
  return eventMulticaster ;
}

通过这种方式也可以实现事件处理程序异步执行。而这种方式的实现原理如下:

容器启动中的核心方法refresh中

public abstract class AbstractApplicationContext {
  public void refresh() {
    // 初始化事件广播器
    initApplicationEventMulticaster();
  }
  protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    // 判断容器中是否存在beanName=applicationEventMulticaster
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
      this.applicationEventMulticaster =
          beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
    } else {
      // 如果不存在则创建一个同步的执行器。
      this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
      beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
    }
  }
}

以上是本篇文章的全部内容,希望对你有帮助。

完毕!!!

责任编辑:武晓燕 来源: Spring全家桶实战案例源码
相关推荐

2013-04-01 15:25:41

异步编程异步EMP

2023-07-31 08:05:30

Spring任务调度

2022-02-11 08:54:33

SpringWeb 应用执行表单

2020-07-02 07:44:27

Spring教程异步

2023-02-07 07:25:36

Spring事件驱动

2010-08-04 13:23:29

Flex事件

2015-09-07 14:08:32

Java编程异步事件

2013-03-08 09:33:25

JavaScript同步异步

2020-11-01 17:10:46

异步事件开发前端

2021-06-26 14:59:13

SpringTransaction执行

2009-07-22 10:13:31

异步ActionASP.NET MVC

2024-07-31 15:57:41

2024-10-15 10:28:43

2021-05-31 07:30:47

Connectsocket函数

2024-01-08 08:45:07

Spring容器Bean

2010-04-06 15:20:56

ASP.NET MVC

2009-08-20 17:47:54

C#异步编程模式

2023-02-26 10:59:51

2020-01-02 16:30:02

Spring BootJava异步请求

2023-09-18 07:46:28

点赞
收藏

51CTO技术栈公众号