专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

SpringBoot事件发布与订阅

在日常开发中,经常会遇到一个方法执行完毕,要通知另一个方法。
比如用户注册了之后需要给他发邮件。这种一个主要的业务,包含了很多附属的业务的情况, 如果对事务要求不是很严格,可以试试SpringBoot的事件发布与订阅。

事件类

首先你需要定义一个事件,这个类继承ApplicationEvent这个抽象类。

import org.springframework.context.ApplicationEvent;

/**
 * 定义一个自定义事件,继承ApplicationEvent类
 * * @author lww * @date 2020-04-09 17:41 */ public class MyApplicationEvent extends ApplicationEvent { private static final long serialVersionUID = 1L; public MyApplicationEvent(Object source) { super(source); System.err.println("发布事件:source = " + source); } } 

发布事件

发布事件很简单,注入 ApplicationContext,调用 context.publishEvent(new MyApplicationEvent("发布事件啦"));就好了,这样一个事件就发布出去了。

import com.ler.eventdemo.event.MyApplicationEvent;
import javax.annotation.Resource;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
 /** * @author lww * @date 2020-04-09 18:05 */ @RestController public class HelloController { @Resource private ApplicationContext context; @GetMapping("/hello") public String hello(String name) { context.publishEvent(new MyApplicationEvent("发布事件啦")); return "Hello " + name; } } 

89_1.png 在发布事件这里有一个小图标,点击就会跳到事件订阅的地方(在idea里)。

订阅事件

有了事件,又发布了事件,接下来就是订阅事件。也很简单,你只需要写一个listener

import com.ler.eventdemo.event.MyApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

/** * 定义一个事件监听器 MyApplicationListener * * @author lww * @date 2020-04-09 17:42 */ @Component public class MyApplicationListener { @EventListener public void onApplicationEvent(MyApplicationEvent event) { System.err.println("接收到事件:" + event.getClass()); } } 

有的说要实现 ApplicationListener 接口,测试后发现,不需要实现什么接口。只要加@EventListener注解,然后参数指定哪个事件,就可以了。

89_2.png 在订阅事件这里,也有一个小图标,点击会跳到发布事件那里(在idea里)。

@TransactionalEventListener

@TransactionalEventListener@EventListener差了一个 Transactional,这个事务表示的意思是, 事件的发送时机可以和事务绑定。

  • TransactionPhase.BEFORE_COMMIT 在提交前
  • TransactionPhase.AFTER_COMMIT 在提交后
  • TransactionPhase.AFTER_ROLLBACK 在回滚后
  • TransactionPhase.AFTER_COMPLETION 在事务完成后

默认 TransactionPhase.AFTER_COMMIT

指定发布时机避免的情况就是,比如注册用户,包含了一些耗时的操作,而这些操作中有异步非阻塞的, 当执行到了发布事件的方法时。用户可能还没有创建完成,此时如果事件发布了,在监听器那边执行时,可能获取用户失败。 而如果在事务提交后执行,就不会出现这种情况。

这个注解 不是 说发布事件的方法和监听器响应方法之间有什么事务关系。他们之间还是没有事务的。无法保证原子性,一致性。

如果要实现事务也不是没有办法,可以先保证 事件的发布方执行完毕,事务提交完成。然后订阅方遵循幂等性规则, 如果订阅方失败,进入重试机制。有点像RocketMQ分段提交,事务回查与重试机制。可以按照这个思想实现。

原理

  • ApplicationContext 接口继承了 ApplicationEventPublisher 接口,所以有 publishEvent方法,可以用于发布任务。
  • ApplicationListener接口继承了 EventListener接口,其中有一个 onApplicationEvent方法,用来监听事件。

org.springframework.context.support.AbstractApplicationContext#refresh方法中, org.springframework.context.support.AbstractApplicationContext#registerListeners里面

89_3.png

可以看到,注册监听器的时候是查找实现了ApplicationListener的接口,那我们没有实现,又是如何注册的呢?

89_4.png

@EventListener注释里有这一句,看这个类的这个方法 org.springframework.context.event.EventListenerMethodProcessor#processBean

89_5.png

在这里,查找使用了@EventListener注解的方法,找到后同样会添加到ConfigurableApplicationContext(ApplicationContext的实现类)中, 作为listener。所以不实现ApplicationListener,同样可以正常使用。
注意的是需要使用Java配置类,如果使用xml配置,则要添加 <context:annotation-config/> 或者 <context:component-scan/>

@TransactionalEventListener包含@EventListener

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@EventListener
public @interface TransactionalEventListener {
 ...... } 

@TransactionalEventListenerorg.springframework.transaction.event.TransactionalEventListenerFactory来处理, 在这个方法中org.springframework.transaction.event.TransactionalEventListenerFactory#createApplicationListener 创建了org.springframework.transaction.event.ApplicationListenerMethodTransactionalAdapter#ApplicationListenerMethodTransactionalAdapterorg.springframework.transaction.event.ApplicationListenerMethodTransactionalAdapter#onApplicationEvent这个方法中,使用TransactionSynchronizationEventAdapter来管理事务。

总结

SpringBoot 事件的发布订阅,使用还是非常简单方便的,在SpringBoot框架中也应用广泛,小伙伴们快快练起来吧。

本文使用 tech.souyunku.com 排版

文章永久链接:https://tech.souyunku.com/33754

未经允许不得转载:搜云库技术团队 » SpringBoot事件发布与订阅

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们