分析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行的方法:
1 private static ImmutableList<Method> getAnnotatedMethods(Class<?> clazz) {2 return subscriberMethodsCache.getUnchecked(clazz);3 }
我們看看這個
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 });
1 private static ImmutableList<Method> getAnnotatedMethodsNotCached(Class<?> clazz) { 2 Set<? extends Class<?>> 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 }
第4行遍歷這個Set
第5行遍歷每個類別的所有方法
第6行呼叫Method的isAnnotationPresent方法判斷目標方法帶有@Subscribe註解,並且這個方法不能是「複合方法」
第16行把複合條件的方法放到map去,並在21行回傳!
以上是guava eventbus實例代碼詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!