序言
在开发中,有时候会使用到发布订阅模式,之前一直以为只要MQ才能实现,后来了解到Spring也可以通过自己的Event事件来实现发布订阅,相比MQ功能会弱很多,但是也会相对简单很多。
Spring原生的使用方式,有兴趣的小伙伴可以研究一下。实际开发可以使用Guava的EventBus以及org.greenrobot的EventBus,两者名字相同,功能也很相似,我使用的后者greenrobot。
学习过程中踩的坑:
- 依赖分不清:各种博客都在说guava的EventBus是多么的厉害,但是使用的依赖却不是guava的;
- 功能讲不清:原谅小白的我确实再翻遍了各种博客后更加懵逼,大部分实现我都是去官网上看了事例才明白的。
1、添加依赖
<dependency>
<groupId>org.greenrobot</groupId>
<artifactId>eventbus</artifactId>
<version>3.2.0</version>
</dependency>
<!-- 有兴趣研究guava的朋友使用下面的依赖(本事例不需要下面的依赖),一个来自Google的强大工具包 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>22.0</version>
</dependency>
2、文档资料
传送门:eventbus文档
小提示:英文网站,可以使用谷歌浏览器翻译,基本误差不大。
3、使用
3.1 配置EventBus
@Configuration
public class EventConfig {
// 使用@Bean配置EventBus,以后直接注入使用即可
@Bean
public EventBus eventBus() {
return EventBus.getDefault();
}
}
3.2 定义事件
// 定义事件,使用普通的POJO即可,这里之所以定义两个pojo,并且字段名一样。
// 就是我之前有个疑问:事件驱动的时候会不会错乱
public class LoginEvent implements Serializable {
private static final long serialVersionUID = -6787105919337013565L;
private String telePhone;
private String email;
private String goodsName;
}
public class OrderEvent implements Serializable {
private static final long serialVersionUID = 8794979353646765379L;
private String telePhone;
private String email;
private String goodsName;
}
3.3 准备订阅者
订阅者实现事件处理方法(也称为“订阅者方法”),在事件发布时将被调用。这些通过@Subscribe注释定义。
注意,使用EventBus 3可以自由选择方法名称(没有像EventBus 2那样的命名约定)。
@Component
public class LoginListener {
// 同步方法
@Subscribe
public void orderTel(LoginEvent loginEvent) {
ThreadUtil.sleep(3000L);
System.out.println("接收到登录消息++发送短信,telePhone="
+ loginEvent.getTelePhone() + ", goodsName=" + loginEvent.getGoodsName()
+ "== " + LocalDateTime.now());
}
// 同步方法
@Subscribe
public void orderEmail(LoginEvent loginEvent) {
ThreadUtil.sleep(3000L);
System.out.println("接收到登录消息++发送邮件,email="
+ loginEvent.getEmail() + ", goodsName=" + loginEvent.getGoodsName() +
"== " + LocalDateTime.now());
}
}
@Component
public class OrderListener {
// 异步方法
@Subscribe(threadMode = ThreadMode.ASYNC)
public void orderTel(OrderEvent orderEvent) {
ThreadUtil.sleep(3000L);
System.out.println("接收到订单消息--发送短信,telePhone="
+ orderEvent.getTelePhone() + ", goodsName=" + orderEvent.getGoodsName()
+ "== " + LocalDateTime.now());
}
// 异步方法
@Subscribe(threadMode = ThreadMode.ASYNC)
public void orderEmail(OrderEvent orderEvent) {
ThreadUtil.sleep(3000L);
System.out.println("接收到订单消息--发送邮件,email="
+ orderEvent.getEmail() + ", goodsName=" + orderEvent.getGoodsName() +
"== " + LocalDateTime.now());
}
}
3.4 注册或注销总线
用户也需要注册和注销总线。这样消息订阅者才能收到事件。
@Component
public class EventHandler {
@Autowired
private EventBus eventBus;
@Autowired
private OrderListener orderListener;
@Autowired
private LoginListener loginListener;
/**
* 注册总线
*/
@PostConstruct
public void init() {
eventBus.register(orderListener);
eventBus.register(loginListener);
}
/**
* 注销总线
*/
@PreDestroy
public void destroy() {
eventBus.unregister(orderListener);
eventBus.unregister(loginListener);
}
/**
* 发布活动
* @param obj 事件
*/
public void eventPost(Object obj){
eventBus.post(obj);
System.out.println("分发消息完成");
ThreadUtil.sleep(4000L);
}
}
3.5 发布活动
主要是调用上一步的eventPost方法,现在模拟一个接口来调用:
@RestController
public class EventCotroller {
@Autowired
private EventHandler eventHandler;
@GetMapping("/eventPost")
public String eventPost() {
LoginEvent loginEvent = new LoginEvent("22222222", "1111@qq.com", "Java实战1111");
OrderEvent event = new OrderEvent("13751528565", "742457@qq.com", "Java实战");
eventHandler.eventPost(loginEvent);
eventHandler.eventPost(event);
return "ok";
}
}
运行结果
接收到登录消息++发送邮件,email=1111@qq.com, goodsName=Java实战1111== 2020-12-20T17:38:54.844
接收到登录消息++发送短信,telePhone=22222222, goodsName=Java实战1111== 2020-12-20T17:38:57.845
分发消息完成
分发消息完成
接收到订单消息–发送邮件,email=742457@qq.com, goodsName=Java实战== 2020-12-20T17:39:00.860
接收到订单消息–发送短信,telePhone=13751528565, goodsName=Java实战== 2020-12-20T17:39:00.860
可以看出使用了异步注解@Subscribe(threadMode = ThreadMode.ASYNC)
的Order事件执行更加高效。
4、结尾
更多操作可参考开头的官网,带你领略事件驱动的更多方式。