#EventBus(二) 上一章讲解了Guava的EventBus的使用,这一章会开始模拟EventBus的源码来实现一个简单EventBus。首先要了解事件总线的几个功能模块。demo在github上。
##1.Subscriber Subscriber模块要做的事情就是根据接收到的事件,来执行相应的操作。这里通过Java的反射来实现,通过调用Method的invoke方法来执行一个类中的某个方法。
###2.1 订阅者 Subscriber.java
//订阅者
public class Subscriber {
//订阅事件的类
private final Object target;
//订阅事件的方法
private final Method method;
public Subscriber(Object target, Method method) {
if (target == null || method == null) {
throw new IllegalArgumentException("Target object and method must not be null");
}
this.target = target;
this.method = method;
//值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查,可以提高性能
this.method.setAccessible(true);
}
public void invoke(Object parameter) throws Exception {
method.invoke(target, parameter);
}
}
###2.2 订阅者的注解 Subscribe.java 这里要模拟EventBus中的Subscribe注解,使用注解的好处是被订阅的消息不需要特定地遵守一些约定,只要标注上这个注解,那么就代表订阅这个类型的消息。
/**
* 标识订阅者
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public [@interface](https://my.oschina.net/u/996807) Subscribe {
}
##2.EventBus EventBus模块要做的就是
* 维护一个根据事件找到对应的订阅者的路由
* 维护一个存储消息的队列
###2.3 事件总线 EventBus.java
//事件总线
public class EventBus {
//维护key是事件的class,value是这个事件所有的订阅者的映射关系
private Map<Class<?>, List<Subscriber>> register;
//存放事件的阻塞队列
private BlockingQueue<Object> queue;
public EventBus() {
this.register = new HashMap<>();
queue = new LinkedBlockingDeque<>();
new Thread(new Runnable() {
[@Override](https://my.oschina.net/u/1162528)
public void run() {
//事件的消费者
Consumer consumer = new Consumer(queue, register);
consumer.start();
}
}).start();
}
/**
* 把类的信息及其subscriber注册到map中去
*/
public void register(Object listener) {
if (listener == null) {
return;
}
Class<?> clazz = listener.getClass();
//找到当前类的所有带有Subscribe注解的方法
for (Method method : getAnnotatedMethod(clazz)) {
pushToResisterMap(listener, method);
}
}
private void pushToResisterMap(Object listener, Method method) {
if (listener == null || method == null) {
return;
}
//获取方法的第一个参数
Class<?> eventParamter = method.getParameterTypes()[0];
List<Subscriber> subscriberList;
if (register.containsKey(eventParamter)) {
subscriberList = register.get(eventParamter);
subscriberList.add(new Subscriber(listener, method));
} else {
subscriberList = new ArrayList<>();
subscriberList.add(new Subscriber(listener, method));
register.put(eventParamter, subscriberList);
}
}
/**
* 获取类的所有带有Subscribe注解的方法
*/
private Set<Method> getAnnotatedMethod(Class<?> clazz) {
Set<Method> annotatedMethods = new HashSet<>();
Method[] methods = clazz.getMethods();
for (Method method : methods) {
Subscribe annotation = method.getAnnotation(Subscribe.class);
if (annotation != null && !method.isBridge()) {
annotatedMethods.add(method);
}
}
return annotatedMethods;
}
/**
* 向阻塞队列里发送事件
*/
public void post(Object event) {
if (event == null) {
return;
}
try {
queue.put(event);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
##3.Consumer 消费者要做的就是不断地从队列中获取新的事件,从EventBus获取这个事件的所有订阅者,然后让订阅者执行相应的方法即可 ###2.4 消费者 Consumer.java //消费者 public class Consumer {
private BlockingQueue<Object> queue;
private Map<Class<?>, List<Subscriber>> register;
public Consumer(BlockingQueue<Object> queue, Map<Class<?>, List<Subscriber>> register) {
this.queue = queue;
this.register = register;
}
public void start() {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (true) {
Object event;
try {
//从队列里取事件,如果没有事件就阻塞住
event = queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
continue;
}
Class<?> clazz = event.getClass();
if (!register.containsKey(clazz)) {
System.out.println("Cannot found event's subscriber");
continue;
}
//找到事件的subscriber,然后执行对应的事件
List<Subscriber> subscriberList = register.get(clazz);
for (Subscriber subscriber : subscriberList) {
try {
subscriber.invoke(event);
} catch (Exception e) {
System.out.println("Eventbus execute event failed , " + event + "/" + subscriber);
}
}
}
}
}
###2.5 订阅事件的服务 EventService.java
public class EventService {
public EventService(EventBus eventBus) {
eventBus.register(this);
}
@Subscribe
public void handleEvent(HelloEvent event) {
if (event == null) {
return;
}
System.out.println("handleEvent received: " + event);
}
@Subscribe
public void doHelloEvent(HelloEvent event) {
if (event == null) {
return;
}
System.out.println("doHelloEvent received: " + event);
}
@Subscribe
public void doGoodByEvent(GoodByEvent event) {
if (event == null) {
return;
}
System.out.println("doGoodByEvent received: " + event.toString());
}
@Subscribe
public void receiveGoodByEvent(GoodByEvent event) {
if (event == null) {
return;
}
System.out.println("receiveGoodByEvent received: " + event.toString());
}
}
class HelloEvent {
private String greeting;
private Date date;
public void setGreeting(String greeting) {
this.greeting = greeting;
}
public void setDate(Date date) {
this.date = date;
}
@Override
public String toString() {
return "HelloEvent{" +
"greeting='" + greeting + '\'' +
", date=" + date +
'}';
}
}
class GoodByEvent {
private String saying;
private Date date;
public void setSaying(String saying) {
this.saying = saying;
}
public void setDate(Date date) {
this.date = date;
}
@Override
public String toString() {
return "GoodByEvent{" +
"saying='" + saying + '\'' +
", date=" + date +
'}';
}
}
###2.6 测试一下
public class UnitTest {
private EventBus eventBus;
private EventService eventService;
@Before
public void init() {
eventBus = new EventBus();
eventService = new EventService(eventBus);
}
private void postSeveralEvent() {
HelloEvent helloEvent = new HelloEvent();
helloEvent.setGreeting("你好啊");
helloEvent.setDate(new Date());
GoodByEvent goodByEvent = new GoodByEvent();
goodByEvent.setSaying("再见啦");
goodByEvent.setDate(new Date());
eventBus.post(helloEvent);
eventBus.post(goodByEvent);
}
@Test
public void run() throws Exception {
postSeveralEvent();
while (Thread.activeCount() > 2) {
}
}
}
运行以上测试用例可以看到,事件被成功的接收到并执行了
doHelloEvent received: HelloEvent{greeting='你好啊', date=Sun May 07 21:13:11 CST 2017}
handleEvent received: HelloEvent{greeting='你好啊', date=Sun May 07 21:13:11 CST 2017}
doGoodByEvent received: GoodByEvent{saying='再见啦', date=Sun May 07 21:13:11 CST 2017}
receiveGoodByEvent received: GoodByEvent{saying='再见啦', date=Sun May 07 21:13:11 CST 2017}
这两篇博客是之前在记录在印象笔记上的笔记,最近有空整理了一下~等有空了还想写一下关于Guice和Jersey的一些博客,之前的公司用了这两个框架。(:зゝ∠)