观察者模式也叫发布订阅模式,这种模式在我们日常开发中很是常见,以不同的形式存在于我们的项目中,消息对列就是一个很好的例子。
java中将这种模式封装了一下,将其内置在jdk中,java.util.Observable,java.util.Observer,
java自身的观察者模式实现还是较为简略,本文主要讲google的guava中实现的观察者模式的使用,以及碰到的一些问题,故转载以下内容,
Java内置的观察者模式, 是通过继承父类, 实现观察者模式的几个主要函数:
Observerable(可被观察的): 是一个父类(class),addObserver(), 添加观察者; deleteObserver(), 删除观察者;
notifyObservers(), 通知观察者;setChanged(), 确认更改;
Observer(观察者): 是一个接口(interface), update(), 更新观察者数据;
setChanged()->notifyObservers(), 必须要先使用setChanged(), 再使用notifyObservers(), 即先确认提交, 再通知观察者;
观察者的更新接口update(Observerable o, Object arg), 即可以使用推(push), 也可以使用拉(pull);
如果notifyObservers(arg), 传递参数, 则为推(push)的方法, 如果没有参数, 则为拉(pull)的方式, 即使用get()方法获取;
观察者的通知(notify)顺序是先入后出的模式.
————————————————
版权声明:本文为CSDN博主「SpikeKing」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/caroline_wendy/article/details/26601659
guava使用说明:
导入maven依赖:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
发布类PublishEventUtil :
package com.zhou.cfets.common.event;
import com.google.common.eventbus.EventBus;
public class PublishEventUtil {
private final static EventBus EVENT_BUS = new EventBus();
/**
* 注册一个对象
*
* @param o
*/
public static void register(Object o) {
EVENT_BUS.register(o);
}
/**
* 取消注册
*
* @param o
*/
public static void unregister(Object o) {
EVENT_BUS.unregister(o);
}
/**
* 发布事件
*
* @param event session logon,logout,onerror
*/
public static void publishCodeEvent(int event) {
EVENT_BUS.post(event);
}
/**
* 发布事件
*
* @param event session logon,logout,onerror
*/
public static void publishEvent(Object event) {
EVENT_BUS.post(event);
}
}
消费基类AbstractSubscribe :
package com.zhou.cfets.common.event;
import com.google.common.eventbus.DeadEvent;
import com.google.common.eventbus.Subscribe;
public abstract class AbstractSubscribe {
@Subscribe
public abstract void onEvent(Integer event);
@Subscribe
public abstract void onEvent(DeadEvent event);
}
(问题一:细心地朋友可能看到我发布的类,发布的事件是int,订阅的却是Integer的类型,这个问题稍后再说明)
启动测试类:
package com.zhou.cfets.common.event;
import com.google.common.eventbus.DeadEvent;
public class PublishEventTest {
public static void main(String[] args) throws InterruptedException {
PublishEventUtil.register(new AbstractSubscribe() {
@Override
public void onEvent(DeadEvent event) {
// TODO Auto-generated method stub
System.out.println("DeadEvent " + event );
}
@Override
public void onEvent(Integer event) {
// TODO Auto-generated method stub
System.out.println("Integer " + event );
}
});
PublishEventUtil.publishCodeEvent(111);
PublishEventUtil.publishEvent("DeadEvent");
Thread.sleep(5000L);
}
}
运行结果:
到此示例结束。
现在我们回归到问题一,如果在消费消息的时候将消费基类AbstractSubscribe的OnEvent的方法Integer类型改为int类型的话,是不能消费事件的,有疑惑的朋友可以试一下,那么为什么呢?
我们查看guava的com.google.common.eventbus.EventBus.post源码发现,post接收的是一个Object的对象,那么java在转换的的时候就将int类型的自动装箱成了Intger类型,
/**
* Posts an event to all registered subscribers. This method will return successfully after the
* event has been posted to all subscribers, and regardless of any exceptions thrown by
* subscribers.
*
* <p>If no subscribers have been subscribed for {@code event}'s class, and {@code event} is not
* already a {@link DeadEvent}, it will be wrapped in a DeadEvent and reposted.
*
* @param event event to post.
*/
public void post(Object event) {
//通过event的类型去获取订阅类,找到正常消费消息,如果没有读到则发布DeadEvent事件
Iterator<Subscriber> eventSubscribers = subscribers.getSubscribers(event);
if (eventSubscribers.hasNext()) {
dispatcher.dispatch(event, eventSubscribers);
} else if (!(event instanceof DeadEvent)) {
// the event had no subscribers and was not itself a DeadEvent
post(new DeadEvent(this, event));
}
}
/**
* Gets an iterator representing an immutable snapshot of all subscribers to the given event at
* the time this method is called.
*/
Iterator<Subscriber> getSubscribers(Object event) {
//这里通过Object的getClass获取订阅对象的运行时对象,进而获取相关的订阅类
ImmutableSet<Class<?>> eventTypes = flattenHierarchy(event.getClass());
List<Iterator<Subscriber>> subscriberIterators =
Lists.newArrayListWithCapacity(eventTypes.size());
for (Class<?> eventType : eventTypes) {
CopyOnWriteArraySet<Subscriber> eventSubscribers = subscribers.get(eventType);
if (eventSubscribers != null) {
// eager no-copy snapshot
subscriberIterators.add(eventSubscribers.iterator());
}
}
return Iterators.concat(subscriberIterators.iterator());
}
自动装箱机制我们可以做一个实验,如下,我们得到int却是变成了Integer对象
public class Test {
private static void getIntClass(Object a) {
System.out.println(a.getClass());
}
public static void main(String[] args) {
getIntClass(1);
}
}
如果错误还请指正,谢谢!