SpringBoot事件发布与监听,代码更优雅

前言

  在大部分的日常开发中,我们经常会遇到类似以下一些场景:

/**
     * 用户注册
     */
    @PostMapping("/userRegister")
    public String userRegister(UserVo userVo) {
        //校验参数1
        //存库2
        //发送邮件3
        //发送短信4
        //API返回结果
        return "操作成功!";
    }

  在这个过程中我们就会发现:用户注册与信息推送强耦合,用户注册其实到存库成功,就已经算是完成了,后面的信息推送都是额外的操作,甚至信息推送失败报错,还会影响API接口的结果,如果在同一事务,报错信息不捕获,还会导致事务回滚,存库失败。

解决方案
第一步:创建一个异步线程池

/**
 * @author by Guoshun
 * @version 1.0.0
 * @description 配置线程池
 * @date 2023/10/17 11:16
 */
@Configuration
public class TaskPoolConfig {

    @Bean("asyncTaskExecutor")
    public Executor myTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);//核心线程数量,线程池创建时候初始化的线程数
        executor.setMaxPoolSize(15);//最大线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        executor.setQueueCapacity(200);//缓冲队列,用来缓冲执行任务的队列
        executor.setKeepAliveSeconds(60);//当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
        executor.setThreadNamePrefix("user-register-task-");//设置好了之后可以方便我们定位处理任务所在的线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);//用来设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
        executor.setAwaitTerminationSeconds(60);//该方法用来设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
        //线程池对拒绝任务的处理策略:这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }
}
第二步:创建一个事件监听处理方法
/**
 * 事件监听
 */
@Slf4j
@Component
public class EventListenerList {

    /**
     * 用户注册事件监听
     */
    @Async("asyncTaskExecutor")
    @EventListener
    @Order(1)//一个事件多个事监听,同步的情况下,使用@order值越小,执行顺序优先
    public void userRegisterListener(UserVo userVo){
        log.info("用户注册事件监听1:"+userVo);
        //开展其他业务,例如发送邮件、短信等
    }
}
第三步:事件发布
/**
 * 事件发布
 */
@Slf4j
@RestController
@RequestMapping("/eventPublish/")
public class EventPublish {

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    /**
     * 用户注册
     */
    @PostMapping("userRegister")
    public String userRegister(UserVo userVo) {
        log.info("用户注册!");
        //发布 用户注册事件
        applicationEventPublisher.publishEvent(userVo);
        return "操作成功!";
    }
}

  至此全部完成,将代码解耦,提高代码可读性。相比于实现ApplicationListener增加EventResouce这种方式少了几个类,但是大致实现都差不多。
参考文章:
一 、@TransactionalEventListener 事务事件监听进行业务解耦
二、SpringBoot系列——事件发布与监听

最后

我需要说明的是,我将全部基础代码(完全能用)已经全部给了出来。但我希望看这个文章的朋友能更多的自己思考,比如结合事务的问题,参考文章中给出了很好的事务管理方式;
第二就是事务的问题:最原始的方法,代码如下:(这种方式用gpt的话说就是:这种方式不清晰、不简洁,并且不符合 Spring 的现代编程模型)

//publishEvent前加入这个代码。等事务提交之后执行发布。
TransactionSynchronizationManager.registerSynchronization(
	new TransactionSynchronizationAdapter() {
		@Override
		public void afterCommit() {
			// applicationEventPublisher.publishEvent(userVo);
 			eventPublisher.publishEvent(new AfterTransactionEvent(entity));
		}
});
//entity 是要发布的实体类

白话异步线程池导致事务不回滚等方式:关注此订阅栏后续增加!

你可能感兴趣的:(SpringBoot系列,spring,boot,python,后端)