4

Spring事件如何异步执行?

 6 months ago
source link: https://www.51cto.com/article/783621.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Spring事件如何异步执行?

作者:Springboot实战案例锦集 2024-03-13 14:35:33
从Spring 4.2开始,事件基础设施得到了显著改进,并提供了基于注释的模型以及发布任意事件的能力(也就是说,不一定是从ApplicationEvent扩展的对象)。当这样的对象被发布时,我们将它包装在一个事件中。

环境:SpringBoot2.7.16

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全家桶实战案例源码

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK