Spring注解驱动之ApplicationListener异步处理事件说明

目录
  • 概述
  • 示例
  • 自定义事件多波器
  • 测试用例
  • ApplicationListener异步执行源码分析

概述

之前我们讲过简单使用ApplicationListener发布事件,处理事件,但是发现是同一个线程发送事件并自己处理事件的。

下面我们就来说下如何使用自定义的线程池来异步处理接收的事件。

示例

实现一个ApplicationListener用于处理事件

package com.atguigu.ext;

import org.springframework.context.ApplicationListener;
import org.springframework.context.PayloadApplicationEvent;
import org.springframework.stereotype.Component;

@Component
public class MyApplicationListener implements ApplicationListener<PayloadApplicationEvent> {

    public void onApplicationEvent(PayloadApplicationEvent applicationEvent) {
        System.out.println("exe thread start:" + Thread.currentThread().getName() + ", time:" + System.currentTimeMillis());
        System.out.println("收到事件:" + applicationEvent);
        System.out.println(applicationEvent.getPayload());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("exe thread end:" + Thread.currentThread().getName() + ", time:" + System.currentTimeMillis());
    }
}

自定义事件多波器

package com.atguigu.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.SimpleApplicationEventMulticaster;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Configuration
@ComponentScan("com.atguigu.ext")
public class ExtConfig {

    @Bean
    public SimpleApplicationEventMulticaster applicationEventMulticaster() {
        SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
        BlockingQueue<Runnable> blockingQueue = new LinkedBlockingDeque<>(1000);
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 10, TimeUnit.SECONDS, blockingQueue);
        simpleApplicationEventMulticaster.setTaskExecutor(threadPoolExecutor);
        return simpleApplicationEventMulticaster;
    }
}

之前看源码可以发现,在容器创建的refresh方法中的initApplicationEventMulticaster()方法执行时,先从容器中获取name为applicationEventMulticaster的组件,如果获取不到就好创建一个默认的applicationEventMulticaster组件,该组件默认是不会设置taskExecutor任务执行器的,所以这里我们自定义一个设置了TaskExecutor的多波器,当执行initApplicationEventMulticaster方法从beanFactory中获取applicationEventMulticaster组件时,走getBean逻辑。

BeanFactory.getBean()逻辑是先从容器查看是否有该组件,如果没有获取该组件的定义,如果有定义就会创建一个组件返回并把组件保存到容器中。

测试用例

package com.atguigu;

import com.atguigu.config.ExtConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @Description :
 * @Version : V1.0.0
 * @Date : 2022/9/1 15:07
 */
public class AnnotationTest {
    public static void main(String[] args) {
        final AnnotationConfigApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(ExtConfig.class);
        System.out.println("main thread start:" + Thread.currentThread().getName() + ", time:" + System.currentTimeMillis());
        applicationContext.publishEvent("发送事件");
        System.out.println("main thread end:" + Thread.currentThread().getName() + ", time:" + System.currentTimeMillis());
        applicationContext.close();
    }
}

测试结果

main thread start:main, time:1663499481185
main thread end:main, time:1663499481188
exe thread start:pool-1-thread-1, time:1663499481188
收到事件:org.springframework.context.PayloadApplicationEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@5ebec15: startup date [Sun Sep 18 19:11:20 CST 2022]; root of context hierarchy]
发送事件
九月 18, 2022 7:11:21 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@5ebec15: startup date [Sun Sep 18 19:11:20 CST 2022]; root of context hierarchy
exe thread end:pool-1-thread-1, time:1663499484198

通过测试结果可以看出,main线程很快就返回了,而实际处理事件的线程是pool-1-thread-1,等待了3s多才返回。

ApplicationListener异步执行源码分析

参考:Spring注解驱动之ApplicationListener用法

与上面同步执行不同的地方就是使用了自定义的多波器里面的线程池执行了事件处理。

多波器的获取。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 深入理解Spring注解@Async解决异步调用问题

    序言:Spring中@Async 根据Spring的文档说明,默认采用的是单线程的模式的.所以在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的. 那么当多个任务的执行势必会相互影响.例如,如果A任务执行时间比较长,那么B任务必须等到A任务执行完毕后才会启动执行.又如在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在spring3.x之后,已经内置了@Async来完美解决这个问题. 1. 何为异步调用? 在解释之前,我们先来看二

  • Spring里的Async注解实现异步操作的方法步骤

    异步执行一般用来发送一些消息数据,数据一致性不要求太高的场景,对于spring来说,它把这个异步进行了封装,使用一个注解就可以实现. 何为异步调用? 在解释异步调用之前,我们先来看同步调用的定义:同步就是整个处理过程顺序执行,当各个过程都执行完毕,并返回结果. 异步调用则是只是发送了调用的指令,调用者无需等待被调用的方法完全执行完毕:而是继续执行下面的流程.例如, 在某个调用中,需要顺序调用 A, B, C三个过程方法:如他们都是同步调用,则需要将他们都顺序执行完毕之后,方算作过程执行完毕: 如

  • Spring中注解方式的异步请求

    一.Servlet3.0异步请求 @WebServlet(value = "/async", asyncSupported = true) public class HelloAsyncServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //

  • SpringBoot实现异步事件驱动的方法

    在项目实际开发过程中,我们有很多这样的业务场景:一个事务中处理完一个业务逻辑后需要跟着处理另外一个业务逻辑,伪码大致如下: @Service public class ProductServiceImpl { ... public void saveProduct(Product product) { productMapper.saveOrder(product); notifyService.notify(product); } ... } 很简单并且很常见的一段业务逻辑:首先将产品先保存数

  • Spring注解驱动之ApplicationListener异步处理事件说明

    目录 概述 示例 自定义事件多波器 测试用例 ApplicationListener异步执行源码分析 概述 之前我们讲过简单使用ApplicationListener发布事件,处理事件,但是发现是同一个线程发送事件并自己处理事件的. 下面我们就来说下如何使用自定义的线程池来异步处理接收的事件. 示例 实现一个ApplicationListener用于处理事件 package com.atguigu.ext; import org.springframework.context.Applicati

  • Spring注解驱动之ApplicationListener用法解读

    目录 ApplicationListener概述 ApplicationListener作用 ApplicationListener用法 ApplicationListener概述 ApplicationListener按照字面意思,它应该是Spring里面的应用监听器,也就是Spring为我们提供的基于事件驱动开发的功能. 也就是说我们只要写一个监听器来实现ApplicationListener接口,该接口中泛型就是我们要监听的事件,那么如果我们发布对应的事件,就能在实现的监听器里面处理. A

  • 详解Spring注解驱动开发之属性赋值

    一.@Value注解 在Person的属性上使用@Value注解指定注入值 public class Person { @Value("#{20-2}") //SpEL表达式 #{} private Integer id; @Value("张三") //基本数据类型 private String name; } 配置类 @Configuration public class MainConfigOfPropertyValues { @Bean public Pers

  • Spring注解驱动扩展原理BeanFactoryPostProcessor

    1.扩展原理-BeanFactoryPostProcessor BeanFactoryPostProcessor * 扩展原理: * BeanPostProcessor:bean后置处理器,bean创建对象初始化前后进行拦截工作的 * * 1.BeanFactoryPostProcessor:beanFactory的后置处理器: * 在BeanFactory标准初始化之后调用,来定制和修改BeanFactory的内容: * 所有的bean定义已经保存加载到beanFactory,但是bean的实

  • Spring注解驱动开发实现属性赋值

    前言 在实际开发当中,Spring中bean的属性直接赋值用的不是太多,整理这方面的资料,做一个小结,以备后续更深入的学习. 通过配置文件的方式 以配置文件的方式启动spring容器时,可以使用property标签的value给bean的属性赋值,赋值的形式有以下几种: <--通过context:property-placeholder将properties文件中的值加载的环境变量中(properties中的属性值最终是以环境变量的形式存储的)> <context:property-pl

  • Spring注解驱动之AOP功能测试

    前言 Spring的AOP指的是在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式[动态代理]. AOP功能测试 ①导入AOP模块 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.3.12.RELEASE</version> </depend

  • Spring注解驱动之BeanDefinitionRegistryPostProcessor原理解析

    目录 BeanDefinitionRegistryPostProcessor概述 案例实践 源码分析 小结 BeanDefinitionRegistryPostProcessor概述 可以看到BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子接口. 注释中说执行时机是所有合法的bean定义已经加载,但是还没实例化. 看起来和BeanFactoryPostProcessor执行时机差不多,但是BeanFactoryPostP

  • Spring Boot集成教程之异步调用Async

    前言 本文主要给大家介绍了关于Spring Boot集成之异步调用Async的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 什么是异步调用? 异步调用是相对于同步调用而言的,同步调用是指程序按预定顺序一步步执行,每一步必须等到上一步执行完后才能执行,异步调用则无需等待上一步程序执行完即可执行. 异步处理方式 调用之后,不返回任何数据. 调用之后,返回数据,通过Future来获取返回数据 如何实现异步调用? 多线程,这是很多人第一眼想到的关键词,没错,多线程就是一种实

  • Spring中的使用@Async异步调用方法

    目录 使用@Async异步调用方法 Async简介 给Spring的TaskExecutor去完成 本次记录Async使用场景 异步请求与异步调用的区别 异步请求的实现 SpringBoot中异步调用的使用 使用@Async异步调用方法 Async简介 异步方法调用使用场景:处理日志.发送邮件.短信...... spring中提供了@Async来实现异步方法. @Async修饰类,则该类所有方法都是异步的,@Async修饰方法,则该方法是异步的. 被修饰的方法在被调用时,会在一个新的线程中执行.

随机推荐