【轉載請註明做者和原文連接,歡迎討論,相互學習。】html
1、前言網絡
ExtensionLoader類,主要是根據擴展點名稱來對擴展點接口實現進行的一系列操做,若是獲取擴展點接口實現實例、適配類實例、更新實現實例等等。app
ExtensionLoader類是dubbo對JDK SPI作了擴展,主要體如今方面:框架
(1)jdk spi僅僅經過接口類名獲取全部實現,而ExtensionLoader則經過接口類名和key值獲取一個實現;學習
(2)Adaptive實現,就是生成一個代理類,這樣就能夠根據實際調用時的一些參數動態決定要調用的類了。url
(3)自動包裝實現,這種實現的類通常是自動激活的,經常使用於包裝類,好比Protocol的兩個實現類:ProtocolFilterWrapper、ProtocolListenerWrapper。 三、url總線設計 Dubbo爲了使得各層解耦,採用了url總線的設計。咱們一般的設計會把層與層之間的交互參數作成Model,這樣層與層之間溝通成本比較大,擴展起來也比較麻煩。所以,Dubbo把各層之間的通訊都採用url的形式。好比,註冊中心啓動時,參數的url爲: registry://0.0.0.0:9090?codec=registry&transporter=netty 這就表示當前是註冊中心,綁定到全部ip,端口是9090,解析器類型是registry,使用的底層網絡通訊框架是netty。spa
2、代碼分析設計
這邊說明若是獲取擴展點接口實現實例步驟流程做爲說明重點,其餘的相似,套路都差很少。具體流程以下:代理
1.獲取ExtensionLoader實例netty
1 public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { 2 if (type == null) 3 throw new IllegalArgumentException("Extension type == null"); 4 if(!type.isInterface()) { 5 throw new IllegalArgumentException("Extension type(" + type + ") is not interface!"); 6 } 7 if(!withExtensionAnnotation(type)) { 8 throw new IllegalArgumentException("Extension type(" + type + 9 ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!"); 10 } 11 12 ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); 13 if (loader == null) { 14 EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); 15 loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); 16 } 17 return loader; 18 }
這個方法有以下幾個重點:
1)判斷是否爲擴展點接口
2)是否帶有SPI註解
3)該擴展點若是爲null,則新建該擴展實現實例並返回。
2.獲取指定擴展點名稱對應的實例
1 /** 2 * 返回指定名字的擴展。若是指定名字的擴展不存在,則拋異常 {@link IllegalStateException}. 3 * 4 * @param name 5 * @return 6 */ 7 @SuppressWarnings("unchecked") 8 public T getExtension(String name) { 9 if (name == null || name.length() == 0) 10 throw new IllegalArgumentException("Extension name == null"); 11 if ("true".equals(name)) { 12 return getDefaultExtension(); 13 } 14 Holder<Object> holder = cachedInstances.get(name); 15 if (holder == null) { 16 cachedInstances.putIfAbsent(name, new Holder<Object>()); 17 holder = cachedInstances.get(name); 18 } 19 Object instance = holder.get(); 20 if (instance == null) { 21 synchronized (holder) { 22 instance = holder.get(); 23 if (instance == null) { 24 instance = createExtension(name); 25 holder.set(instance); 26 } 27 } 28 } 29 return (T) instance; 30 }
這個步驟稍微複雜,咱們詳細分解一下:
1)9~13行,對擴展點名稱進行合法性判斷,若是是true,返回默認的擴展點接口實現。
2)14~19行,在cachedInstances中put一個存放擴展點接口實現實例的對象。
3)24行是建立擴展點接口實現的關鍵,方法:createExtension。
1 private T createExtension(String name) { 2 Class<?> clazz = getExtensionClasses().get(name); 3 if (clazz == null) { 4 throw findException(name); 5 } 6 try { 7 T instance = (T) EXTENSION_INSTANCES.get(clazz); 8 if (instance == null) { 9 EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance()); 10 instance = (T) EXTENSION_INSTANCES.get(clazz); 11 } 12 injectExtension(instance); 13 Set<Class<?>> wrapperClasses = cachedWrapperClasses; 14 if (wrapperClasses != null && wrapperClasses.size() > 0) { 15 for (Class<?> wrapperClass : wrapperClasses) { 16 instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); 17 } 18 } 19 return instance; 20 } catch (Throwable t) { 21 throw new IllegalStateException("Extension instance(name: " + name + ", class: " + 22 type + ") could not be instantiated: " + t.getMessage(), t); 23 } 24 }
這個方法的關鍵在於,也就是咱們須要的實現類了:Class<?> clazz = getExtensionClasses().get(name);
它會從META-INF/services/,META-INF/dubbo/,META-INF/dubbo/internal/這個三個目錄下掃描全部的擴展點信息,存放到extensionClasses這個Map<String, Class<?>> 裏邊,
而後咱們根據擴展點接口名稱就能夠獲取到擴展點接口實現實例了,具體代碼以下。
1 private Map<String, Class<?>> getExtensionClasses() { 2 Map<String, Class<?>> classes = cachedClasses.get(); 3 if (classes == null) { 4 synchronized (cachedClasses) { 5 classes = cachedClasses.get(); 6 if (classes == null) { 7 classes = loadExtensionClasses(); 8 cachedClasses.set(classes); 9 } 10 } 11 } 12 return classes; 13 }
private Map<String, Class<?>> loadExtensionClasses() { final SPI defaultAnnotation = type.getAnnotation(SPI.class); if(defaultAnnotation != null) { String value = defaultAnnotation.value(); if(value != null && (value = value.trim()).length() > 0) { String[] names = NAME_SEPARATOR.split(value); if(names.length > 1) { throw new IllegalStateException("more than 1 default extension name on extension " + type.getName() + ": " + Arrays.toString(names)); } if(names.length == 1) cachedDefaultName = names[0]; } } Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>(); loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY); loadFile(extensionClasses, DUBBO_DIRECTORY); loadFile(extensionClasses, SERVICES_DIRECTORY); return extensionClasses; }