在使用ApplicationEvent和Listener快速实现业务解耦中提到了用Spring提供的观察者设计模式完成系统内部逻辑解耦。本文将介绍Google-Guava中的一种消息发布-订阅类库——EventBus。
EventBus 是Google.Guava提供的消息发布-订阅类库,它实现了观察者设计模式,消息通知负责人通过EventBus去注册/注销观察者,最后由消息通知负责人给观察者发布消息。
前提:在pom.xml中引入guava包
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.0-jre</version>
</dependency>
新建类:
相当于注册中心:注册执行都是通过这个类:
import com.google.common.eventbus.EventBus;
public class EventBusCenter {
private static EventBus eventBus = new EventBus();
private EventBusCenter() {
}
public static EventBus getInstance() {
return eventBus;
}
public static void register(Object obj) {
eventBus.register(obj);
}
public static void unregister(Object obj) {
eventBus.unregister(obj);
}
public static void post(Object obj) {
eventBus.post(obj);
}
}
兴建两个观察者:
import com.google.common.eventbus.Subscribe;
public class DataObserver1 {
/**
* 只有通过@Subscribe注解的方法才会被注册进EventBus
* 而且方法有且只能有1个参数
*
* @param msg
*/
@Subscribe
public void func(String msg) {
System.out.println("String msg: " + msg);
}
}
import com.google.common.eventbus.Subscribe;
/**
* Created by zhangzh on 2017/1/10.
*/
public class DataObserver2 {
/**
* post() 不支持自动装箱功能,只能使用Integer,不能使用int,否则handlersByType的Class会是int而不是Intege
* 而传入的int msg参数在post(int msg)的时候会被包装成Integer,导致无法匹配到
*/
@Subscribe
public void func(Integer msg) {
System.out.println("Integer msg: " + msg);
}
}
可以看到通过 @Subscribe注解只能有一个参数,如果是两个参数就会报错,只能有一个参数:
Exception in thread "main" com.google.common.util.concurrent.UncheckedExecutionException: java.lang.IllegalArgumentException: Method public void com.study.eventbus.DataObserver1.func(java.lang.String,java.lang.String) has @Subscribe annotation but has 2 parameters.Subscriber methods must have exactly 1 parameter.
at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2050)
at com.google.common.cache.LocalCache.get(LocalCache.java:3952)
at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:3974)
at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4958)
at com.google.common.cache.LocalCache$LocalLoadingCache.getUnchecked(LocalCache.java:4964)
at com.google.common.eventbus.SubscriberRegistry.getAnnotatedMethods(SubscriberRegistry.java:173)
at com.google.common.eventbus.SubscriberRegistry.findAllSubscribers(SubscriberRegistry.java:164)
at com.google.common.eventbus.SubscriberRegistry.register(SubscriberRegistry.java:74)
at com.google.common.eventbus.EventBus.register(EventBus.java:186)
at com.study.eventbus.EventBusCenter.register(EventBusCenter.java:21)
at com.study.eventbus.Test.main(Test.java:13)
Caused by: java.lang.IllegalArgumentException: Method public void com.study.eventbus.DataObserver1.func(java.lang.String,java.lang.String) has @Subscribe annotation but has 2 parameters.Subscriber methods must have exactly 1 parameter.
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:412)
at com.google.common.eventbus.SubscriberRegistry.getAnnotatedMethodsNotCached(SubscriberRegistry.java:184)
at com.google.common.eventbus.SubscriberRegistry.access$000(SubscriberRegistry.java:54)
at com.google.common.eventbus.SubscriberRegistry$1.load(SubscriberRegistry.java:154)
at com.google.common.eventbus.SubscriberRegistry$1.load(SubscriberRegistry.java:151)
at com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3528)
at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2277)
at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2154)
at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2044)
... 10 more
然后编写测试类:
/**
* Created by zhangzh on 2017/1/10.
*/
public class Test {
public static void main(String[] args) throws InterruptedException {
DataObserver1 observer1 = new DataObserver1();
DataObserver2 observer2 = new DataObserver2();
EventBusCenter.register(observer1);
EventBusCenter.register(observer2);
System.out.println("============ start ====================");
// 只有注册的参数类型为String的方法会被调用
EventBusCenter.post("post string method");
EventBusCenter.post(123);
System.out.println("============ after unregister ============");
// 注销observer2
EventBusCenter.unregister(observer2);
EventBusCenter.post("post string method");
EventBusCenter.post(123);
System.out.println("============ end =============");
}
}
总结:
EventBus的使用注意问题:
- 代码可读性很差,项目中使用的时候,从post的地方,查询handle使用,都是使用ide的搜索服务,问题很难定位,不如普通的接口调用方便查询;
- 由于EventBus是将消息队列放入到内存中的,listener消费这个消息队列,故系统重启之后,保存或者堆积在队列中的消息丢失。
作者:jackcooper
链接:https://www.jianshu.com/p/93486a604c34
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。