學習開源代碼能夠有如下收穫:java
1.學些其架構設計思想,看看怎麼實現一個高可用,可擴展的架構。git
2.學習一些好的java語法,畢竟你在實際代碼過程當中不會使用到全部的java語法,而在看源碼的過程當中,就有可能發現你不曾使用過,但比較巧妙的用法。github
3.學習設計模式,開源代碼經常會使用到一些設計模式,能夠加深你對設計模式的理解。編程
4.學習一些基本設計原則,好比經過有界隊列避免內存溢出,經過異步線程提升性能,經過依賴注入提升擴展性,可測性等等。設計模式
EventBus 是Google的一個開源庫,它利用發佈/訂閱者者模式來對項目進行解耦。它能夠利用不多的代碼,來實現多組件間通訊。架構
EventBus代碼結構以下:併發
類說明:異步
EventBus是核心入口類,若是全都採用默認實現,只須要實現Listener,並經過調用EventBus類的方法則可完成全部功能。ide
SubscriberExceptionHandler是異常處理接口,可替換本身的實現。post
Executor用於異步執行Listener的監聽方法,可替換本身的實現。
Dispatcher是event派發接口,可替換本身的實現,默認提供了3個實現類。
SubscriberRegistry是事件註冊類,也用於獲取訂閱者。
Subscriber是訂閱者,對Listener作了封裝,屏蔽了複雜的調用邏輯,使得使用者沒必要關心這些複雜邏輯,只要提供Listener的具體實現則可。
SynchronizedSubscriber是支持併發調用的訂閱者,能夠經過在Listener的事件監聽方法上添加AllowConcurrentEvents註解來達到使用SynchronizedSubscriber的目的。
Subscribe是一個註解類,能夠在任何類的方法上添加該註解來表達該方法是一個事件監聽方法。
DeadEvent用於記錄那些已經沒有訂閱者的事件。
SubscriberExceptionContext是異常上下文,用於當訂閱者處理異常時記錄相關的上下文信息,方便異常處理實現類得到這些信息來處理異常。
1.面向接口編程:Executor,Dispatcher,SubscriberExceptionHandler都是接口,使用者能夠替換具體的實現類。
2.使用依賴注入,使得可測性加強。從圖中能夠看到EventBus持有Executor,Dispatcher,SubscriberExceptionHandler三個對象,它們可經過EventBus的構造器注入,這樣使用者就有機會方便的替換具體的實現,或者mock一個對象來用於測試。若是它們不是可注入的,而是直接在某個方法內調用,就失去了替換的機會。固然,接口注入的方式還有不少,例如經過set方法,經過反射動態生成等等,但構造器注入是最簡單,最省心的方法。
3.異步處理,提升性能。Subscriber最終處理event是經過Executor以異步線程方式執行,不會由於同步而阻塞,大大提升了性能。
4.利用Subscribe註解+反射,使得任何類的任何方法均可以成爲事件監聽類,而不須要實現特定的監聽者接口。
5.考慮了異常處理機制,一方面提供了SubscriberExceptionHandler接口讓使用者來實現具體的處理邏輯,另一方面提供了DeadEvent類,將那些失去訂閱者的事件統一歸類到DeadEvent,由使用者自行實現一個Listener去處理它們。
6.經過模板方法固化了整個調用流程,使用者只須要按照要求簡單實現則可以使用。
Listener是一個事件監聽類,一個Listener類能夠經過不一樣的方法同時監聽多個事件,任何類均可以做爲Listener,但須要遵循如下要求:
1.必須在監聽方法上添加Subscribe註解,用以表達該方法是一個監聽方法
2.此監聽方法只能有一個參數,這個參數就是要監聽的事件,參數類的Class能夠理解爲就是要監聽的EventType
1.Listener例子
public class MyListener {
// 添加Subscribe註解則表示要監聽某個事件
@Subscribe
public void onEvent(MyEvent1 event1) {
// do something
}
// 一個Listener能夠監聽多個事件
@Subscribe
public void onEvent(MyEvent2 event2) {
// do something
}
}
複製代碼
2.SubscriberRegistry.java:
// 傳入的clazz類就是Listener的Class
private static ImmutableList<Method> getAnnotatedMethodsNotCached(Class<?> clazz) {
Set<? extends Class<?>> supertypes = TypeToken.of(clazz).getTypes().rawTypes();
Map<MethodIdentifier, Method> identifiers = Maps.newHashMap();
for (Class<?> supertype : supertypes) {
for (Method method : supertype.getDeclaredMethods()) {
// 這裏查找方法上是否有Subscribe註解
if (method.isAnnotationPresent(Subscribe.class) && !method.isSynthetic()) {
// TODO(cgdecker): Should check for a generic parameter type and error out
Class<?>[] parameterTypes = method.getParameterTypes();
// 這裏檢查方法的參數只有1個
checkArgument(
parameterTypes.length == 1,
"Method %s has @Subscribe annotation but has %s parameters."
+ "Subscriber methods must have exactly 1 parameter.",
method,
parameterTypes.length);
MethodIdentifier ident = new MethodIdentifier(method);
if (!identifiers.containsKey(ident)) {
identifiers.put(ident, method);
}
}
}
}
return ImmutableList.copyOf(identifiers.values());
}
複製代碼
1.在一個系統中,根據用途不一樣,能夠同時存在多個EventBus,不一樣的EventBus經過identifier來識別。
2.爲方便使用,EventBus提供了多個構造器,使用者能夠根據須要注入不一樣的實現類,最簡單的構造器是一個無參構造器,所有使用默認實現。
3.在實際使用過程當中,可使用一個單例類來持有EventBus實例,若有須要,能夠持有不一樣的EventBus實例用於不一樣的用途。
1.EventBus.java
//無參構造器
public EventBus() {
this("default");
}
//指定標識符構造器
public EventBus(String identifier) {
this(
identifier,
MoreExecutors.directExecutor(),
Dispatcher.perThreadDispatchQueue(),
LoggingHandler.INSTANCE);
}
//注入自定義異常類構造器
public EventBus(SubscriberExceptionHandler exceptionHandler) {
this(
"default",
MoreExecutors.directExecutor(),
Dispatcher.perThreadDispatchQueue(),
exceptionHandler);
}
//注入全部參數構造器,需注意的是,此方法不是public的,只能在包內訪問。
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);
}
複製代碼
以上兩步完成後,便可經過EventBus來註冊Listener。
1.EventBus.java
private final SubscriberRegistry subscribers = new SubscriberRegistry(this);
public void register(Object object) {
subscribers.register(object);
}
複製代碼
2.SubscriberRegistry.java
void register(Object listener) {
// 查找有Subscribe註解的方法,並封裝爲Subscriber,Multimap的key記錄的Class就是要監聽的對象的Class
Multimap<Class<?>, Subscriber> listenerMethods = findAllSubscribers(listener);
for (Entry<Class<?>, Collection<Subscriber>> entry : listenerMethods.asMap().entrySet()) {
Class<?> eventType = entry.getKey();
Collection<Subscriber> eventMethodsInListener = entry.getValue();
CopyOnWriteArraySet<Subscriber> eventSubscribers = subscribers.get(eventType);
if (eventSubscribers == null) {
CopyOnWriteArraySet<Subscriber> newSet = new CopyOnWriteArraySet<>();
eventSubscribers =
MoreObjects.firstNonNull(subscribers.putIfAbsent(eventType, newSet), newSet);
}
eventSubscribers.addAll(eventMethodsInListener);
}
}
複製代碼
發佈事件比較簡單,只要在須要發佈事件的地方獲得EventBus實例,而後調用post方法則可。
1.EventBus.java
public void post(Object event) {
// 根據event找到訂閱者,這裏實際是根據event.Class來查找,也即和Listener的監聽方法的參數的Class一致。
Iterator<Subscriber> eventSubscribers = subscribers.getSubscribers(event);
if (eventSubscribers.hasNext()) {
//經過dispatcher來派發事件,最終調用的是Subscriber的dispatchEvent方法
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));
}
}
複製代碼
2.Subscriber.java
final void dispatchEvent(final Object event) {
// 經過executor來實現異步調用,這個executor在EventBus是可注入的,能夠注入修改後的實現類
executor.execute(
new Runnable() {
@Override
public void run() {
try {
invokeSubscriberMethod(event);
} catch (InvocationTargetException e) {
// 這裏最終調用的是在EventBus中注入的SubscriberExceptionHandler,能夠注入修改後的實現類
bus.handleSubscriberException(e.getCause(), context(event));
}
}
});
}
void invokeSubscriberMethod(Object event) throws InvocationTargetException {
try {
//這裏的method就是Listener的監聽方法,target就是Listener對象,event就是這個監聽方法的入參
method.invoke(target, checkNotNull(event));
} catch (IllegalArgumentException e) {
throw new Error("Method rejected target/argument: " + event, e);
} catch (IllegalAccessException e) {
throw new Error("Method became inaccessible: " + event, e);
} catch (InvocationTargetException e) {
if (e.getCause() instanceof Error) {
throw (Error) e.getCause();
}
throw e;
}
}
複製代碼
end.
Java極客站點: javageektour.com/