SpringBoot事件发布订阅异步配置及原理解析

一、@Async注解方式

1.配置@Async

在启动类上添加@EnableAsync注解开启异步

// 开启异步
@EnableAsync
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

@Asnyc注解配置线程池

@Configuration
public class AsyncConfiguration implements AsyncConfigurer {
	
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(30);
        executor.setQueueCapacity(1024);
        executor.setKeepAliveSeconds(180);
        executor.setThreadNamePrefix("@Async线程-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

2.定义事件

@Setter
@Getter
public class AsyncEvent extends ApplicationEvent {

    private String msg;

    public AsyncEvent(Object source, String msg) {
        super(source);
        this.msg = msg;
    }
}

3.定义监听器

使用@EventListener定义一个监听AsyncEvent的方法,并使用@Async注解进行异步处理

@Component
@Slf4j
public class AsyncListener {

    
	/**
     * 开启异步监听
     * @param asyncEvent 事件
     */
    @Async
    @EventListener
    public void asyncAnnotationListener(AsyncEvent asyncEvent) {
        try {
            // 模拟耗时
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("asyncAnnotationListener接收到消息:{}", asyncEvent.getMsg());
    }
}

4.测试

@SpringBootTest
@Slf4j
public class EventTest {

    @Test
    public void testAsyncAnnotationEvent() {
        log.info("开始发送消息");
        AsyncEvent event = new AsyncEvent(this, "@Async异步消息测试");
        // hutool工具类,也可以使用ApplicationContext#publishEvent方法
        SpringUtil.publishEvent(event);
        log.info("发送消息成功");
        try {
            // 等待listener异步操作完成
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

5.结果

事件发布后未被阻塞,过一段时间后监听器完成操作。

在这里插入图片描述

二、自定义ApplicationEventMulticaster方式

1.配置ApplicationEventMulticaster

往Spring容器中注入自定义 SimpleApplicationEventMulticaster,并设置线程池。注:方法名一定为applicationEventMulticaster,否则不会生效。

@Configuration
public class EventMulticasterConfiguration {
    
    @Bean
    public ApplicationEventMulticaster applicationEventMulticaster(ConfigurableListableBeanFactory configurableListableBeanFactory) {
        SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster(configurableListableBeanFactory);
        // 自定义线程池
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(1024);
        executor.setKeepAliveSeconds(180);
        executor.setThreadNamePrefix("EventMulticaster线程-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        // 给multicaster设置线程池
        multicaster.setTaskExecutor(executor);
        return multicaster;
    }
}

2.定义事件

@Getter
@Setter

public class CustomEvent extends ApplicationEvent {

    private String msg;

    public CustomEvent(Object source, String msg) {
        super(source);
        this.msg = msg;
    }
}

3.定义监听器

同样用@EventListener注解定义一个监听事件的方法,不过不需要添加@Async注解

@Component
@Slf4j
public class AsyncListener {

    @EventListener
    public void customListener(CustomEvent customEvent) {
        try {
            // 模拟耗时
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("customerListener接收到消息:{}", customEvent.getMsg());
    }
}

4.测试

@SpringBootTest
@Slf4j
public class EventTest {

    @Test
    public void testCustomEventMulticaster() {
        log.info("开始发送消息");
        CustomEvent event = new CustomEvent(this, "CustomEventMulticaster异步消息测试");
        SpringUtil.publishEvent(event);
        log.info("发送消息成功");
        try {
            // 等待listener异步操作完成
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

5.结果

事件发布后未被阻塞,过一段时间后监听器完成操作。

在这里插入图片描述

三、不同

@Async注解比较灵活,适合项目中既有异步又有同步的场景;而自定义ApplicationEventMulticaster并配置线程池的方式适合只有异步的场景,一旦配置则全局只能异步。

四、原理

SpringBoot利用ApplicationEventMulticaster进行事件的广播,实现类为SimpleApplicationEventMulticaster。容器启动时,会对ApplicationEventMulticaster进行初始化,在AbstractApplicationContext#refresh方法中调用initApplicationEventMulticaster()方法进行事件多播器初始化:

public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";	

protected void initApplicationEventMulticaster() {
    	// 获取IOC容器
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    	// 判断容器中是否存在名为applicationEventMulticaster的Bean
		if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
            // 如果容器中存在则直接用容器中的ApplicationEventMulticaster(在此处拓展)
			this.applicationEventMulticaster =
					beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
			if (logger.isTraceEnabled()) {
				logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
			}
		}
		else {
            // 如果容器中不存在则创建一个SimpleApplicationEventMulticaster,此处创建的Multicaster是没有设置线程池的
			this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
            // 将创建的SimpleApplicationEventMulticaster注入容器中
			beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
			if (logger.isTraceEnabled()) {
				logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
						"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
			}
		}
	}

流程:

  1. 判断容器中是否存在名为applicationEventMulticaster的Bean,如果有则直接使用容器中的bean。这就是上面为什么自定义ApplicationEventMulticaster的时候方法名只能为applicationEventMulticaster的原因。
  2. 如果不存在则创建一个SimpleApplicationEventMulticaster,此时创建的Multicaster是没有设置线程池的(同步)。

发布事件调用的是AbstractApplicationContext中的publishEvent方法 。在publishEvent方法中又调用applicationEventMulticaster类中的multicastEvent方法发布事件。

	protected void publishEvent(Object event, @Nullable ResolvableType typeHint) {
		//......
        
		if (this.earlyApplicationEvents != null) {
			this.earlyApplicationEvents.add(applicationEvent);
		}
		else if (this.applicationEventMulticaster != null) {
            // 在此处对事件进行广播
			this.applicationEventMulticaster.multicastEvent(applicationEvent, eventType);
		}

		//......
	}

SimpleApplicationEventMulticaster#multicastEvent

	@Override
	public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : ResolvableType.forInstance(event));
		Executor executor = getTaskExecutor();
        // 遍历事件所有监听器
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            // 线程池不为空(默认为空)且listener支持异步(默认都是支持的)
			if (executor != null && listener.supportsAsyncExecution()) {
				try {
                    // 在线程池中异步调用
					executor.execute(() -> invokeListener(listener, event));
				}
				catch (RejectedExecutionException ex) {
					// Probably on shutdown -> invoke listener locally instead
					invokeListener(listener, event);
				}
			}
			else {
                // 没有配置线程池或者listener不支持异步,则直接调用
				invokeListener(listener, event);
			}
		}
	}
	protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
		ErrorHandler errorHandler = getErrorHandler();
		if (errorHandler != null) {
			try {
				doInvokeListener(listener, event);
			}
			catch (Throwable err) {
				errorHandler.handleError(err);
			}
		}
		else {
			doInvokeListener(listener, event);
		}
	}

	@SuppressWarnings({"rawtypes", "unchecked"})
	private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
            // 直接调用监听器中的方法
			listener.onApplicationEvent(event);
		}
		catch (ClassCastException ex) {
			String msg = ex.getMessage();
			if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
					(event instanceof PayloadApplicationEvent payloadEvent &&
							matchesClassCastMessage(msg, payloadEvent.getPayload().getClass()))) {
				// Possibly a lambda-defined listener which we could not resolve the generic event type for
				// -> let's suppress the exception.
				Log loggerToUse = this.lazyLogger;
				if (loggerToUse == null) {
					loggerToUse = LogFactory.getLog(getClass());
					this.lazyLogger = loggerToUse;
				}
				if (loggerToUse.isTraceEnabled()) {
					loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
				}
			}
			else {
				throw ex;
			}
		}
	}

SimpleApplicationEventMulticaster#multicastEvent先判断是否配置了线程池,如果配置了线程池,则在线程池用异步调用,如果没有配置线程池则直接调用。

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值