分析guava eventbus之前,先看一下传统观察者模式的写法:
subject接口是抽象主题,相当于被观察者,它持有一个监听者observer的列表,attach方法往这个列表里面注册监听者,detach方法注销监听者,notify方法用于事件发生时通知到列表中的监听者
通常在notify的实现方法中会调用监听者的update方法。
observer是抽象观察者,带一个update方法,update方法被具体主题的notify方法调用。
这是一种传统的针对接口的编程方法。与之不同的是eventbus里面采用“”隐式接口”,一种基于java annotation的编程方式。
区别在于:这种“隐式接口”的对应关系是在程序运行时产生的,而基于真正意义接口与实现的是在编译时就建立的对应关系,相比之下,“隐式接口”则更加灵活
我们分析下隐式接口以及实现是怎么建立绑定关系的,看代码:
1 ##subscriberregistry类的register方法 2 void register(object listener) { 3 multimap<class<?>, subscriber> listenermethods = findallsubscribers(listener); 4 5 for (map.entry<class<?>, collection<subscriber>> entry : listenermethods.asmap().entryset()) { 6 class<?> eventtype = entry.getkey(); 7 collection<subscriber> eventmethodsinlistener = entry.getvalue(); 8 9 copyonwritearrayset<subscriber> eventsubscribers = subscribers.get(eventtype);10 11 if (eventsubscribers == null) {12 copyonwritearrayset<subscriber> newset = new copyonwritearrayset<subscriber>();13 eventsubscribers = moreobjects.firstnonnull(14 subscribers.putifabsent(eventtype, newset), newset);15 }16 17 eventsubscribers.addall(eventmethodsinlistener);18 }19 }
这个方法中有用的就是第3行,其余的代码大致分析一下,就是便利这个mutimap,将同一类型的事件的监听者subsriber添加到对应的set当中,如果当前类型事件的subsriber的set是空,那么先添加一个空的set.
跟进以上第3行的方法:
1 /** 2 * returns all subscribers for the given listener grouped by the type of event they subscribe to. 3 */ 4 private multimap<class<?>, subscriber> findallsubscribers(object listener) { 5 multimap<class<?>, subscriber> methodsinlistener = hashmultimap.create(); 6 class<?> clazz = listener.getclass(); 7 for (method method : getannotatedmethods(clazz)) { 8 class<?>[] parametertypes = method.getparametertypes(); 9 class<?> eventtype = parametertypes[0];10 methodsinlistener.put(eventtype, subscriber.create(bus, listener, method));11 }12 return methodsinlistener;13 }
这个方法中核心的方法是第7行,获取特定class的带subscribe注解的所有方法,其余代码的意思是拿到这些方法后,放到多值map当中,然后返回。
跟进以上第7行的方法:
1 private static immutablelist<method> getannotatedmethods(class<?> clazz) {2 return subscribermethodscache.getunchecked(clazz);3 }
跟倒这里后,eclipse没得跟了,怀疑是内部匿名类调用了某个方法相关联(eclipse对内部匿名类的调用链路也显示不全)
我们看看这个
subscribermethodscache
1 private static final loadingcache<class<?>, immutablelist<method>> subscribermethodscache =2 cachebuilder.newbuilder()3 .weakkeys()4 .build(new cacheloader<class<?>, immutablelist<method>>() {5 @override6 public immutablelist<method> load(class<?> concreteclass) throws exception {7 return getannotatedmethodsnotcached(concreteclass);8 }9 });
load方法被调用的时候,调用了 当前类的 getannotatedmethodsnotcached方法,跟进去这个方法:
1 private static immutablelist<method> getannotatedmethodsnotcached(class<?> clazz) { 2 set supertypes = typetoken.of(clazz).gettypes().rawtypes(); 3 map<methodidentifier, method> identifiers = maps.newhashmap(); 4 for (class<?> supertype : supertypes) { 5 for (method method : supertype.getdeclaredmethods()) { 6 if (method.isannotationpresent(subscribe.class) && !method.issynthetic()) { 7 // todo(cgdecker): should check for a generic parameter type and error out 8 class<?>[] parametertypes = method.getparametertypes(); 9 checkargument(parametertypes.length == 1,10 method %s has @subscribe annotation but has %s parameters.11 + subscriber methods must have exactly 1 parameter.,12 method, parametertypes.length);13 14 methodidentifier ident = new methodidentifier(method);15 if (!identifiers.containskey(ident)) {16 identifiers.put(ident, method);17 }18 }19 }20 }21 return immutablelist.copyof(identifiers.values());22 }
第2行的意思是拿到当前类自己+当前类的父类货接口 的所有类,放在一个set中
第4行遍历这个set
第5行遍历每个类的所有方法
第6行调用method的isannotationpresent方法判断目标方法带有 @subscribe注解,并且 该方法不能是 “复合方法”
第16行把复合条件的方法放到map中去,并在21行返回!
以上就是guava eventbus实例代码详解的详细内容。