欢迎您的访问
专注架构,Java,数据结构算法,Python技术分享

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 {
 ...... } 

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

总结

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

赞(0) 打赏
版权归原创作者所有,任何形式转载请联系作者;码农code之路 » SpringBoot事件发布与订阅

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏