likes
comments
collection
share

SpringBoot事件发布与订阅

作者站长头像
站长
· 阅读数 26

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

}

SpringBoot事件发布与订阅 在发布事件这里有一个小图标,点击就会跳到事件订阅的地方(在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注解,然后参数指定哪个事件,就可以了。

SpringBoot事件发布与订阅 在订阅事件这里,也有一个小图标,点击会跳到发布事件那里(在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里面

SpringBoot事件发布与订阅

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

SpringBoot事件发布与订阅

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

SpringBoot事件发布与订阅

在这里,查找使用了@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框架中也应用广泛,小伙伴们快快练起来吧。

本文使用 mdnice 排版

转载自:https://juejin.cn/post/6844904190590844936
评论
请登录