经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring » 查看文章
SpringBoot实现异步事件驱动的方法
来源:jb51  时间:2021/6/28 12:41:47  对本文有异议

在项目实际开发过程中,我们有很多这样的业务场景:一个事务中处理完一个业务逻辑后需要跟着处理另外一个业务逻辑,伪码大致如下:

  1. @Service
  2. public class ProductServiceImpl {
  3. ...
  4. public void saveProduct(Product product) {
  5. productMapper.saveOrder(product);
  6. notifyService.notify(product);
  7. }
  8. ...
  9. }

很简单并且很常见的一段业务逻辑:首先将产品先保存数据库,然后发送通知。

某一天你们可能需要把新增的产品存到Es中,这时候也需要代码可能变成这样:

  1. @Service
  2. public class ProductServiceImpl {
  3. ...
  4. public void saveProduct(Product product) {
  5. productMapper.saveProduct(product);
  6. esService.saveProduct(product)
  7. notifyService.notify(product);
  8. }
  9. ...
  10. }

随着业务需求的变化,代码也需要跟着一遍遍的修改。而且还会存在另外一个问题,如果通知系统挂了,那就不能再新增产品了。

对于上面这种情况非常适合引入消息中间件(消息队列)来对业务进行解耦,但并非所有的业务系统都会引入消息中间件(引入会第三方架构组件会带来很大的运维成本)。

Spring提供了事件驱动机制可以帮助我们实现这一需求。

Spring事件驱动

spring事件驱动由3个部分组成

  • ApplicationEvent:表示事件本身,自定义事件需要继承该类,用来定义事件
  • ApplicationEventPublisher:事件发送器,主要用来发布事件
  • ApplicationListener:事件监听器接口,监听类实现ApplicationListener 里onApplicationEvent方法即可,也可以在方法上增加@EventListener以实现事件监听。

实现Spring事件驱动一般只需要三步:

  • 自定义需要发布的事件类,需要继承ApplicationEvent类
  • 使用ApplicationEventPublisher来发布自定义事件
  • 使用@EventListener来监听事件

这里需要特别注意一点,默认情况下事件是同步的。即事件被publish后会等待Listener的处理。如果发布事件处的业务存在事务,监听器处理也会在相同的事务中。如果需要异步处理事件,可以onApplicationEvent方法上加@Aync支持异步或在有@EventListener的注解方法上加上@Aync。

源码实战

创建事件

  1. public class ProductEvent extends ApplicationEvent {
  2. public ProductEvent(Product product) {
  3. super(product);
  4. }
  5. }

发布事件

  1. @Service
  2. public class ProductServiceImpl implements IproductService {
  3. ...
  4. @Autowired
  5. private ApplicationEventPublisher publisher;
  6. @Override
  7. @Transactional(rollbackFor = Exception.class)
  8. public void saveProduct(Product product) {
  9. productMapper.saveProduct(product);
  10. //事件发布
  11. publisher.publishEvent(product);
  12. }
  13. ...
  14. }

事件监听

  1. @Slf4j
  2. @AllArgsConstructor
  3. public class ProductListener {
  4.  
  5. private final NotifyService notifyServcie;
  6.  
  7. @Async
  8. @Order
  9. @EventListener(ProductEvent.class)
  10. public void notify(ProductEvent event) {
  11. Product product = (Product) event.getSource();
  12. notifyServcie.notify(product, "product");
  13. }
  14. }

在SpringBoot启动类上增加@EnableAsync 注解

  1. @Slf4j
  2. @EnableSwagger2
  3. @SpringBootApplication
  4. @EnableAsync
  5. public class ApplicationBootstrap {
  6. ...
  7. }

使用了Async后会使用默认的线程池SimpleAsyncTaskExecutor,一般我们会在项目中自定义一个线程池。

  1. @Configuration
  2. public class ExecutorConfig {
  3. /** 核心线程数 */
  4. private int corePoolSize = 10;
  5. /** 最大线程数 */
  6. private int maxPoolSize = 50;
  7. /** 队列大小 */
  8. private int queueCapacity = 10;
  9. /** 线程最大空闲时间 */
  10. private int keepAliveSeconds = 150;
  11.  
  12. @Bean("customExecutor")
  13. public Executor myExecutor() {
  14. ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  15. executor.setCorePoolSize(corePoolSize);
  16. executor.setMaxPoolSize(maxPoolSize);
  17. executor.setQueueCapacity(queueCapacity);
  18. executor.setThreadNamePrefix("customExecutor-");
  19. executor.setKeepAliveSeconds(keepAliveSeconds);
  20.  
  21. // rejection-policy:当pool已经达到max size的时候,如何处理新任务
  22. // CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行
  23. executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
  24. executor.initialize();
  25. return executor;
  26. }
  27. }
  28.  

到此这篇关于SpringBoot实现异步事件驱动的方法的文章就介绍到这了,更多相关SpringBoot 异步事件驱动内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号