在开发中,有时候会使用到发布订阅模式,之前一直以为只要MQ才能实现,后来了解到Spring也可以通过自己的Event事件来实现发布订阅,相比MQ功能会弱很多,但是也会相对简单很多。
Spring原生的使用方式,有兴趣的小伙伴可以研究一下。实际开发可以使用Guava的EventBus以及org.greenrobot的EventBus,两者名字相同,功能也很相似,我使用的后者greenrobot。
学习过程中踩的坑:
org.greenrobot
eventbus
3.2.0
com.google.guava
guava
22.0
传送门:eventbus文档
小提示:英文网站,可以使用谷歌浏览器翻译,基本误差不大。
@Configuration
public class EventConfig {
// 使用@Bean配置EventBus,以后直接注入使用即可
@Bean
public EventBus eventBus() {
return EventBus.getDefault();
}
}
// 定义事件,使用普通的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;
}
订阅者实现事件处理方法(也称为“订阅者方法”),在事件发布时将被调用。这些通过@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());
}
}
用户也需要注册自己和注销从总线。只有在注册订户时,他们才会收到事件。
@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);
}
}
主要是调用上一步的eventPost方法,现在模拟一个接口来调用:
@RestController
public class EventCotroller {
@Autowired
private EventHandler eventHandler;
@GetMapping("/eventPost")
public String eventPost() {
LoginEvent loginEvent = new LoginEvent("22222222", "[email protected]", "Java实战1111");
OrderEvent event = new OrderEvent("13751528565", "[email protected]", "Java实战");
eventHandler.eventPost(loginEvent);
eventHandler.eventPost(event);
return "ok";
}
}
接收到登录消息++发送邮件,[email protected], goodsName=Java实战1111== 2020-12-20T17:38:54.844
接收到登录消息++发送短信,telePhone=22222222, goodsName=Java实战1111== 2020-12-20T17:38:57.845
分发消息完成
分发消息完成
接收到订单消息–发送邮件,[email protected], goodsName=Java实战== 2020-12-20T17:39:00.860
接收到订单消息–发送短信,telePhone=13751528565, goodsName=Java实战== 2020-12-20T17:39:00.860
可以看出使用了异步注解@Subscribe(threadMode = ThreadMode.ASYNC)
的Order事件执行更加高效。
更多操作可参考开头的官网,带你领略事件驱动的更多方式。