文章目录
一、概述
EventBus
是Google.Guava提供的消息发布-订阅类库,它实现了观察者设计模式,消息通知负责人通过EventBus去注册/注销观察者,最后由消息通知负责人给观察者发布消息。
二、观察者设计模式
1、目的
定义对象间的一种一对多依赖关系,当一个对象的状态发生改变时候,所有依赖于它的对象都得到通知并被自动更新。
2、应用实例
- 接水的时候,接水的人观察水位,当水位到一定程度时候便停止接水;
- 老师一布置任务,学生听到都得做。
3、代码
package com.gary.designpattern.observe;
/**
* 观察者接口IListenner
* 定义一个update()方法,当被观察者调用notifyListenner()方法时,观察者的update()方法会被回调
* @author gary
*
*/
public interface IListenner {
public void update(String info);
}
package com.gary.designpattern.observe;
/**
* 被观察者接口ISpeaker
* 声明添加删除通知观察者方法
* @author gary
*
*/
public interface ISpeaker {
public void addListenner(IListenner listenner);
public void removeListenner(IListennerlistenner);
public void notifyListenner();
}
package com.gary.designpattern.observe;
public class Student implements IListenner {
private String name;
private String info;
public Student(String name) {
this.name = name;
}
@Override
public void update(String info) {
this.info = info ;
read();
}
public void read() {
System.out.println("学生:" + name + " 接收到老师的消息 :" + info);
}
}
package com.gary.designpattern.observe;
import java.util.ArrayList;
import java.util.List;
public class Teacher implements ISpeaker {
private List<IListenner> listennerList;
private String info;
public Teacher() {
listennerList = new ArrayList<>();
}
@Override
public void addListenner(IListenner listenner) {
listennerList.add(listenner);
}
@Override
public void removeListenner(IListennerlistenner) {
listennerList.remove(listenner);
}
@Override
public void notifyListenner() {
for(int i = 0; i < listennerList.size();i++) {
IListenner lis = listennerList.get(i);
lis.update(info);
}
}
public void setInfo(String info) {
System.out.println("老师发送消息 : " +info);
this.info = info;
notifyListenner();
}
}
测试:
package com.gary.designpattern.observe;
public class Test {
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.addListenner(new Student("张三"));
teacher.addListenner(new Student("李四"));
teacher.addListenner(new Student("王五"));
teacher.setInfo("大家好");
}
}
结果:
老师发送消息 : 大家好
学生:张三 接收到老师的消息 :大家好
学生:李四 接收到老师的消息 :大家好
学生:王五 接收到老师的消息 :大家好
三、EventBus使用
1、引入依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
2、示例
public class EventBusCenter {
private static EventBus eventBus = new EventBus();
private EventBusCenter() {}
public static EventBus getInstance() {
return eventBus;
}
public static void register(Object object) {
eventBus.register(object);
}
public static void unregister(Object object) {
eventBus.unregister(object);
}
public static void post(Object object) {
eventBus.post(object);
}
}
public class DataListenerTwo {
@Subscribe
public void fun(Integer msg) {
System.out.println("Two fun Integer msg: " + msg);
}
@Subscribe
public void fun2(Integer msg) {
System.out.println("Two fun2 Integer msg: " + msg);
}
}
public class DataListenerOne {
@Subscribe
public void fun(String msg) {
System.out.println("One fun String msg: " + msg);
}
@Subscribe
public void fun2(String msg) {
System.out.println("One fun2 String msg: " + msg);
}
}
public class TestForEventBus {
public static void main(String[] args) {
DataListenerOne one = new DataListenerOne();
DataListenerTwo two = new DataListenerTwo();
EventBusCenter.register(one);
EventBusCenter.register(two);
EventBusCenter.post("post string");
EventBusCenter.post(123);
EventBusCenter.unregister(two);
EventBusCenter.post("post string");
EventBusCenter.post(123);
}
}
One fun2 String msg: post string
One fun String msg: post string
Two fun Integer msg: 123
Two fun2 Integer msg: 123
One fun2 String msg: post string
One fun String msg: post string
3、注意事项
- 只有通过@Subscribe注解的方法才会被注册金EventBus
- 方法只能有1个参数
- 使用基本数据类型的包装类,否则会无法匹配
4、带来的问题
由于EventBus是将消息队列放入到内存中的,listener消费这个消息队列,所以系统重启之后,保存或者堆积在队列中的消息丢失。
解决思路:把内存中的数据持久化到磁盘中。
5、原理图
6、注意
每次register后都需要进行一次unregister,因为如果不unregister,会让对象无法得到内存回收,导致内存泄漏。所以必须在unregister方法中释放对象所占的内存。
四、源码分析
1、构造方法
默认构造方法:
/**
* Creates a new EventBus named "default".
*/
public EventBus() {
this("default");
}
/**
* Creates a new EventBus with the given {@code identifier}.
*
* @param identifier a brief name for this bus, for logging purposes. Should
* be a valid Java identifier.
*/
public EventBus(String identifier) {
this(identifier, MoreExecutors.directExecutor(),
Dispatcher.perThreadDispatchQueue(), LoggingHandler.INSTANCE);
}
EventBus(String identifier, Executor executor, Dispatcher dispatcher,
SubscriberExceptionHandler exceptionHandler) {
this.identifier = checkNotNull(identifier);
this.executor = checkNotNull(executor);
this.dispatcher = checkNotNull(dispatcher);
this.exceptionHandler = checkNotNull(exceptionHandler);
}
2、订阅者(register)
public void register(Object object) {
subscribers.register(object);
}
/**
* 添加订阅者
* Registers all subscriber methods on the given listener object.
*/
void register(Object listener) {
//找到该对象下的所有订阅者
Multimap<Class<?>, Subscriber> listenerMethods = findAllSubscribers(listener);
//遍历订阅者map
for (Map.Entry<Class<?>, Collection<Subscriber>> entry : listenerMethods.asMap().entrySet()) {
Class<?> eventType = entry.getKey();
Collection<Subscriber> eventMethodsInListener = entry.getValue();
//将遍历得到的每一个订阅者添加到对应事件类型的Copy-On-Write容器中
CopyOnWriteArraySet<Subscriber> eventSubscribers = subscribers.get(eventType);
if (eventSubscribers == null) {
CopyOnWriteArraySet<Subscriber> newSet = new CopyOnWriteArraySet<Subscriber>();
//putIfAbsent将新事件类型对应set加到map,如果map中存在则返回原来的value,如果不存在加入新的后则返回null
//如果返回值为null,那么使用newSet
eventSubscribers = MoreObjects.firstNonNull(
subscribers.putIfAbsent(eventType, newSet), newSet);
}
//把该事件所有订阅者加入该事件的set中
eventSubscribers.addAll(eventMethodsInListener);
}
}
/**
* 返回map,以事件类型(方法第一个参数类型)为键,订阅者(以eventBus,listener对象,以及method封装而成的订阅者对象)为值
* Returns all subscribers for the given listener grouped by the type of event they subscribe to.
*/
private Multimap<Class<?>, Subscriber> findAllSubscribers(Object listener) {
Multimap<Class<?>, Subscriber> methodsInListener = HashMultimap.create();
Class<?> clazz = listener.getClass();
//将订阅者加入事件对应的multimap中 (Multimap k唯一 v可以放多个)
for (Method method : getAnnotatedMethods(clazz)) {
Class<?>[] parameterTypes = method.getParameterTypes();
Class<?> eventType = parameterTypes[0];
methodsInListener.put(eventType, Subscriber.create(bus, listener, method));
}
return methodsInListener;
}
/**
* All registered subscribers, indexed by event type.
* 事件类型 对应的 所有订阅者
* <p>The {@link CopyOnWriteArraySet} values make it easy and relatively lightweight to get an
* immutable snapshot of all current subscribers to an event without any locking.
*/
private final ConcurrentMap<Class<?>, CopyOnWriteArraySet<Subscriber>> subscribers =
Maps.newConcurrentMap();
3、发布者(post)
/**
* 将事件发部给所有的订阅者
* 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.
* 如果没有订阅者订阅该事件,那么将会用一个DeadEvent事件包装该事件,重发布
* <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) {
//获取事件对应的所有订阅者
Iterator<Subscriber> eventSubscribers = subscribers.getSubscribers(event);
//遍历订阅者
if (eventSubscribers.hasNext()) {
//将事件分发给订阅者
//dispatcher在构造中已经初始化为PerThreadQueuedDispatcher
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));
}
}
PerThreadQueuedDispatcher中的dispatch方法
@Override
void dispatch(Object event, Iterator<Subscriber> subscribers) {
checkNotNull(event);
checkNotNull(subscribers);
Queue<Event> queueForThread = queue.get();
queueForThread.offer(new Event(event, subscribers));
if (!dispatching.get()) {
dispatching.set(true);
try {
Event nextEvent;
while ((nextEvent = queueForThread.poll()) != null) {
while (nextEvent.subscribers.hasNext()) {
//分发事件给订阅者执行
nextEvent.subscribers.next().dispatchEvent(nextEvent.event);
}
}
} finally {
dispatching.remove();
queue.remove();
}
}
}
/**
* Dispatches {@code event} to this subscriber using the proper executor.
*/
final void dispatchEvent(final Object event) {
executor.execute(new Runnable() {
@Override
public void run() {
try {
//订阅者执行事件(反射执行方法,即订阅者为方法,事件为参数)
invokeSubscriberMethod(event);
} catch (InvocationTargetException e) {
bus.handleSubscriberException(e.getCause(), context(event));
}
}
});
}