Dubbo的擴展點框架主要位於com.alibaba.dubbo.common.extension這個包下,其結構以下:java
com.alibaba.dubbo.common.extension | |--factory | |--AdaptiveExtensionFactory | |--SpiExtensionFactory | |--support | |--ActivateComparator | |--Activate #自動激活加載擴展的註解 |--Adaptive #自適應擴展點的註解 |--ExtensionFactory #擴展點對象生成工廠接口 |--ExtensionLoader #擴展點加載器,擴展點的查找,校驗,加載等核心邏輯的實現類 |--SPI #擴展點註解
Dubbo的擴展點主要基於ExtensionLoader這個單例類進行加載,在進行相關擴展時需注意擴展實現的線程安全性),其具備如下特性:緩存
(1)擴展點自動包裝(Wrapper)安全
(2)擴展點自動裝配數據結構
(3)擴展點自適應(Adaptive)app
(4)擴展點自動激活(Active)框架
相關參數緩存在對應的數據結構中,先列出以下,方便後面看源代碼時查閱:函數
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>(); private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String,Class<?>>>(); private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>(); private volatile Class<?> cachedAdaptiveClass = null; private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>(); private String cachedDefaultName; private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>(); private Set<Class<?>> cachedWrapperClasses;
(1)cachedAdaptiveClass : 當前Extension類型對應的AdaptiveExtension類型(只能一個)性能
(2)cachedWrapperClasses : 當前Extension類型對應的全部Wrapper實現類型(無順序)學習
(3)cachedActivates : 當前Extension實現自動激活實現緩存(map,無序)ui
(4)cachedNames : 擴展點實現類對應的名稱(如配置多個名稱則值爲第一個)
當loadExtensionClasses方法執行完成以後,還有一下變量被賦值:
(5)cachedDefaultName : 當前擴展點的默認實現名稱
當getExtensionClasses方法執行完成以後,除了上述變量被賦值以外,還有如下變量被賦值:
(6)cachedClasses : 擴展點實現名稱對應的實現類(一個實現類可能有多個名稱)
其實也就是說,在調用了getExtensionClasses方法以後,當前擴展點對應的實現類的一些信息就已經加載進來了而且被緩存了。後面的許多操做均可以直接經過這些緩存數據來進行處理了。
ExtensionLoader
沒有提供public
的構造方法,可是提供了一個public static
的getExtensionLoader
,這個方法就是獲取ExtensionLoader
實例的工廠方法。其public
成員方法中有三個比較重要的方法:
每個ExtensionLoader
實例僅負責加載特定SPI
擴展的實現*。所以想要獲取某個擴展的實現,首先要獲取到該擴展對應的ExtensionLoader
實例,下面咱們就來看一下獲取ExtensionLoader
實例的工廠方法getExtensionLoader
:
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { if (type == null) throw new IllegalArgumentException("Extension type == null"); if(!type.isInterface()) { throw new IllegalArgumentException("Extension type(" + type + ") is not interface!"); } if(!withExtensionAnnotation(type)) { // 只接受使用@SPI註解註釋的接口類型 throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!"); } // 先從靜態緩存中獲取對應的ExtensionLoader實例 ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); if (loader == null) { EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); // 爲Extension類型建立ExtensionLoader實例,並放入靜態緩存 loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); } return loader; }
該方法須要一個Class
類型的參數,該參數表示但願加載的擴展點類型,該參數必須是接口,且該接口必須被@SPI
註解註釋,不然拒絕處理。檢查經過以後首先會檢查ExtensionLoader緩存中是否已經存在該擴展對應的ExtensionLoader
,若是有則直接返回,不然建立一個新的ExtensionLoader
負責加載該擴展實現,同時將其緩存起來。能夠看到對於每個擴展,dubbo中只會有一個對應的ExtensionLoader
實例。
接下來看下ExtensionLoader
的私有構造函數:
private ExtensionLoader(Class<?> type) { this.type = type; // 若是擴展類型是ExtensionFactory,那麼則設置爲null // 這裏經過getAdaptiveExtension方法獲取一個運行時自適應的擴展類型(每一個Extension只能有一個@Adaptive類型的實現,若是沒有dubbo會動態生成一個類) objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
這裏保存了對應的擴展類型,而且設置了一個額外的objectFactory
屬性,他是一個ExtensionFactory
類型,ExtensionFactory
主要用於加載擴展的實現:
@SPI public interface ExtensionFactory { /** * Get extension. * * @param type object type. * @param name object name. * @return object instance. */ <T> T getExtension(Class<T> type, String name); }
同時ExtensionFactory
也被@SPI
註解註釋,說明他也是一個擴展點,從前面com.alibaba.dubbo.common.extension
包的結構圖中能夠看到,dubbo內部提供了兩個實現類:SpiExtensionFactory
和 AdaptiveExtensionFactory
,實際上還有一個SpringExtensionFactory
,不一樣的實現能夠已不一樣的方式來完成擴展點實現的加載,這塊稍後再來學習。從ExtensionLoader
的構造函數中能夠看到,若是要加載的擴展點類型是ExtensionFactory
是,object
字段被設置爲null。因爲ExtensionLoader
的使用範圍有限(基本上侷限在ExtensionLoader
中),所以對他作了特殊對待:在須要使用ExtensionFactory
的地方,都是經過對應的自適應實現來代替。
默認的ExtensionFactory
實現中,AdaptiveExtensionFactotry
被@Adaptive
註解註釋,也就是它就是ExtensionFactory
對應的自適應擴展實現(每一個擴展點最多隻能有一個自適應實現,若是全部實現中沒有被@Adaptive
註釋的,那麼dubbo會動態生成一個自適應實現類),也就是說,全部對ExtensionFactory
調用的地方,實際上調用的都是AdpativeExtensionFactory
,那麼咱們看下他的實現代碼:
@Adaptive public class AdaptiveExtensionFactory implements ExtensionFactory { private final List<ExtensionFactory> factories; public AdaptiveExtensionFactory() { ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class); List<ExtensionFactory> list = new ArrayList<ExtensionFactory>(); for (String name : loader.getSupportedExtensions()) { // 將全部ExtensionFactory實現保存起來 list.add(loader.getExtension(name)); } factories = Collections.unmodifiableList(list); } public <T> T getExtension(Class<T> type, String name) { // 依次遍歷各個ExtensionFactory實現的getExtension方法,一旦獲取到Extension即返回 // 若是遍歷完全部的ExtensionFactory實現均沒法找到Extension,則返回null for (ExtensionFactory factory : factories) { T extension = factory.getExtension(type, name); if (extension != null) { return extension; } } return null; } }
綜上能夠看到,他會遍歷當前系統中全部的ExtensionFactory
實現來獲取指定的擴展實現,獲取到擴展實現或遍歷完全部的ExtensionFactory
實現。這裏調用了ExtensionLoader
的getSupportedExtensions
方法來獲取ExtensionFactory
的全部實現,又回到了ExtensionLoader
類。
getExtension(name) -> createExtension(name) #若是無緩存則建立 -> getExtensionClasses().get(name) #獲取name對應的擴展類型 -> 實例化擴展類 -> injectExtension(instance) # 擴展點注入 -> instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)) #循環遍歷全部wrapper實現,實例化wrapper並進行擴展點注入
public T getAdaptiveExtension() -> createAdaptiveExtension() #若是無緩存則建立 -> getAdaptiveExtensionClass().newInstance() #獲取AdaptiveExtensionClass -> getExtensionClasses() # 加載當前擴展全部實現,看是否有實現被標註爲@Adaptive -> createAdaptiveExtensionClass() #若是沒有實現被標註爲@Adaptive,則動態建立一個Adaptive實現類 -> createAdaptiveExtensionClassCode() #動態生成實現類java代碼 -> compiler.compile(code, classLoader) #動態編譯java代碼,加載類並實例化 -> injectExtension(instance)
該方法有多個重載方法,不過最終都是調用了三個參數的那一個重載形式。其代碼結構也相對剪短,就不須要在列出概要流程了。
從前面ExtensionLoader
的私有構造函數中能夠看出,在選擇ExtensionFactory
的時候,並非調用getExtension(name)
來獲取某個具體的實現類,而是調用getAdaptiveExtension
來獲取一個自適應的實現。那麼首先咱們就來分析一下getAdaptiveExtension
這個方法的實現吧:
public T getAdaptiveExtension() { Object instance = cachedAdaptiveInstance.get(); // 首先判斷是否已經有緩存的實例對象 if (instance == null) { if(createAdaptiveInstanceError == null) { synchronized (cachedAdaptiveInstance) { instance = cachedAdaptiveInstance.get(); if (instance == null) { try { instance = createAdaptiveExtension(); // 沒有緩存的實例,建立新的AdaptiveExtension實例 cachedAdaptiveInstance.set(instance); } catch (Throwable t) { createAdaptiveInstanceError = t; throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t); } } } } else { throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); } } return (T) instance; }
首先檢查緩存的adaptiveInstance是否存在,若是存在則直接使用,不然的話調用createAdaptiveExtension
方法來建立新的adaptiveInstance而且緩存起來。也就是說對於某個擴展點,每次調用ExtensionLoader.getAdaptiveExtension
獲取到的都是同一個實例。
private T createAdaptiveExtension() { try { return injectExtension((T) getAdaptiveExtensionClass().newInstance()); // 先獲取AdaptiveExtensionClass,在獲取其實例,最後進行注入處理 } catch (Exception e) { throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e); } }
在createAdaptiveExtension
方法中,首先經過getAdaptiveExtensionClass
方法獲取到最終的自適應實現類型,而後實例化一個自適應擴展實現的實例,最後進行擴展點注入操做。先看一個getAdaptiveExtensionClass
方法的實現:
private Class<?> getAdaptiveExtensionClass() { getExtensionClasses(); // 加載當前Extension的全部實現,若是有@Adaptive類型,則會賦值爲cachedAdaptiveClass屬性緩存起來 if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } return cachedAdaptiveClass = createAdaptiveExtensionClass(); // 沒有找到@Adaptive類型實現,則動態建立一個AdaptiveExtensionClass }
他只是簡單的調用了getExtensionClasses
方法,而後在判adaptiveCalss緩存是否被設置,若是被設置那麼直接返回,不然調用createAdaptiveExntesionClass
方法動態生成一個自適應實現,關於動態生成自適應實現類而後編譯加載而且實例化的過程這裏暫時不分析,留到後面在分析吧。這裏咱們看getExtensionClassses
方法:
private Map<String, Class<?>> getExtensionClasses() { Map<String, Class<?>> classes = cachedClasses.get(); // 判斷是否已經加載了當前Extension的全部實現類 if (classes == null) { synchronized (cachedClasses) { classes = cachedClasses.get(); if (classes == null) { classes = loadExtensionClasses(); // 若是尚未加載Extension的實現,則進行掃描加載,完成後賦值給cachedClasses變量 cachedClasses.set(classes); } } } return classes; }
在getExtensionClasses
方法中,首先檢查緩存的cachedClasses,若是沒有再調用loadExtensionClasses
方法來加載,加載完成以後就會進行緩存。也就是說對於每一個擴展點,其實現的加載只會執行一次。loadExtensionClasses
方法在上文已經介紹過,就是讀取對應的配置文件,並將相關配置進行緩存。在此再也不贅述。
(2)、injectExtension
建立自適應擴展點實現類型和實例化就已經完成了,下面就來看下擴展點自動注入的實現:
private T injectExtension(T instance) { try { if (objectFactory != null) { for (Method method : instance.getClass().getMethods()) { if (method.getName().startsWith("set") && method.getParameterTypes().length == 1 && Modifier.isPublic(method.getModifiers())) {// 處理全部set方法 Class<?> pt = method.getParameterTypes()[0];// 獲取set方法參數類型 try { // 獲取setter對應的property名稱 String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : ""; Object object = objectFactory.getExtension(pt, property); // 根據類型,名稱信息從ExtensionFactory獲取 if (object != null) { // 若是不爲空,說set方法的參數是擴展點類型,那麼進行注入 method.invoke(instance, object); } } catch (Exception e) { logger.error("fail to inject via method " + method.getName() + " of interface " + type.getName() + ": " + e.getMessage(), e); } } } } } catch (Exception e) { logger.error(e.getMessage(), e); } return instance; }
這裏能夠看到,擴展點自動注入的一句就是根據setter方法對應的參數類型和property名稱從ExtensionFactory
中查詢,若是有返回擴展點實例,那麼就進行注入操做。到這裏getAdaptiveExtension
方法就分析完畢了。
這個方法的主要做用是用來獲取ExtensionLoader
實例表明的擴展的指定實現。已擴展實現的名字做爲參數,結合前面學習getAdaptiveExtension
的代碼,咱們能夠推測,這方法中也使用了在調用getExtensionClasses
方法的時候收集並緩存的數據,其中涉及到名字和具體實現類型對應關係的緩存屬性是cachedClasses
。具體是是否如咱們猜測的那樣呢,學習一下相關代碼就知道了:
public T getExtension(String name) { if (name == null || name.length() == 0) throw new IllegalArgumentException("Extension name == null"); if ("true".equals(name)) { // 判斷是不是獲取默認實現 return getDefaultExtension(); } Holder<Object> holder = cachedInstances.get(name);// 緩存 if (holder == null) { cachedInstances.putIfAbsent(name, new Holder<Object>()); holder = cachedInstances.get(name); } Object instance = holder.get(); if (instance == null) { synchronized (holder) { instance = holder.get(); if (instance == null) { instance = createExtension(name);// 沒有緩存實例則建立 holder.set(instance);// 緩存起來 } } } return (T) instance; }
接着看createExtension
方法的實現:
private T createExtension(String name) { Class<?> clazz = getExtensionClasses().get(name); // getExtensionClass內部使用cachedClasses緩存 if (clazz == null) { throw findException(name); } try { T instance = (T) EXTENSION_INSTANCES.get(clazz); // 從已建立Extension實例緩存中獲取 if (instance == null) { EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } injectExtension(instance); // 屬性注入 // Wrapper類型進行包裝,層層包裹 Set<Class<?>> wrapperClasses = cachedWrapperClasses; if (wrapperClasses != null && wrapperClasses.size() > 0) { for (Class<?> wrapperClass : wrapperClasses) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } return instance; } catch (Throwable t) { throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type + ") could not be instantiated: " + t.getMessage(), t); } }
從代碼中能夠看到,內部調用了getExtensionClasses
方法來獲取當前擴展的全部實現,而getExtensionClassse
方法會在第一次被調用的時候將結果緩存到cachedClasses
變量中,後面的調用就直接從緩存變量中獲取了。這裏還能夠看到一個緩存EXTENSION_INSTANCES
,這個緩存是ExtensionLoader
的靜態成員,也就是全局緩存,存放着全部的擴展點實現類型與其對應的已經實例化的實例對象(是全部擴展點,不是某一個擴展點),也就是說全部的擴展點實如今dubbo中最多都只會有一個實例。
拿到擴展點實現類型對應的實例以後,調用了injectExtension
方法對該實例進行擴展點注入,緊接着就是遍歷該擴展點接口的全部Wrapper來對真正的擴展點實例進行Wrap操做,都是對經過將上一次的結果做爲下一個Wrapper的構造函數參數傳遞進去實例化一個Wrapper對象,最後總返回回去的是Wrapper類型的實例而不是具體實現類的實例。
這裏或許有一個疑問: 從代碼中看,不論instance
是否存在於EXTENSION_INSTANCE
,都會進行擴展點注入和Wrap操做。那麼若是對於同一個擴展點,調用了兩次createExtension
方法的話,那不就進行了兩次Wrap操做麼?
若是外部可以直接調用createExtension
方法,那麼確實可能出現這個問題。可是因爲createExtension
方法是private
的,所以外部沒法直接調用。而在ExtensionLoader
類中調用它的getExtension
方法(只有它這一處調用),內部本身作了緩存(cachedInstances
),所以當getExtension
方法內部調用了一次createExtension
方法以後,後面對getExtension
方法執行一樣的調用時,會直接使用cachedInstances
緩存而不會再去調用createExtension
方法了。
(4)、getActivateExtension
getActivateExtension
方法主要獲取當前擴展的全部可自動激活的實現。可根據入參(values)調整指定實現的順序,在這個方法裏面也使用到getExtensionClasses
方法中收集的緩存數據。
public List<T> getActivateExtension(URL url, String[] values, String group) { List<T> exts = new ArrayList<T>(); List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values); // 解析配置要使用的名稱 // 若是未配置"-default",則加載全部Activates擴展(names指定的擴展) if (! names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) { getExtensionClasses(); // 加載當前Extension全部實現,會獲取到當前Extension中全部@Active實現,賦值給cachedActivates變量 for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) { // 遍歷當前擴展全部的@Activate擴展 String name = entry.getKey(); Activate activate = entry.getValue(); if (isMatchGroup(group, activate.group())) { // 判斷group是否知足,group爲null則直接返回true T ext = getExtension(name); // 獲取擴展現例 // 排除names指定的擴展;而且若是names中沒有指定移除該擴展(-name),且當前url匹配結果顯示可激活才進行使用 if (! names.contains(name) && ! names.contains(Constants.REMOVE_VALUE_PREFIX + name) && isActive(activate, url)) { exts.add(ext); } } } Collections.sort(exts, ActivateComparator.COMPARATOR); // 默認排序 } // 對names指定的擴展進行專門的處理 List<T> usrs = new ArrayList<T>(); for (int i = 0; i < names.size(); i ++) { // 遍歷names指定的擴展名 String name = names.get(i); if (! name.startsWith(Constants.REMOVE_VALUE_PREFIX) && ! names.contains(Constants.REMOVE_VALUE_PREFIX + name)) { // 未設置移除該擴展 if (Constants.DEFAULT_KEY.equals(name)) { // default表示上面已經加載而且排序的exts,將排在default以前的Activate擴展放置到default組以前,例如:ext1,default,ext2 if (usrs.size() > 0) { // 若是此時user不爲空,則user中存放的是配置在default以前的Activate擴展 exts.addAll(0, usrs); // 注意index是0,放在default前面 usrs.clear(); // 放到default以前,而後清空 } } else { T ext = getExtension(name); usrs.add(ext); } } } if (usrs.size() > 0) { // 這裏留下的都是配置在default以後的 exts.addAll(usrs); // 添加到default排序以後 } return exts; }
Adaptive:由於dubbo底層會大量使用反射,出於性能考慮默認使用javassist字節碼編譯生成一個adaptive,由它動態委派處理。用戶能夠本身實現一個adaptive,只須要對某個類打上@adaptive便可。對於默認編譯生成Adaptive的方案,須要使用@Adaptive聲明接口上的哪些方法是adaptive方法。擴展點名稱的key默認是接口類型上@SPI#value,方法上的@Adaptive#value有更高優先級。
官方文檔描述動態生成的AdaptiveExtension
代碼以下:
package <擴展點接口所在包>; public class <擴展點接口名>$Adpative implements <擴展點接口> { public <有@Adaptive註解的接口方法>(<方法參數>) { if(是否有URL類型方法參數?) 使用該URL參數 else if(是否有方法類型上有URL屬性) 使用該URL屬性 # <else 在加載擴展點生成自適應擴展點類時拋異常,即加載擴展點失敗!> if(獲取的URL == null) { throw new IllegalArgumentException("url == null"); } 根據@Adaptive註解上聲明的Key的順序,從URL獲致Value,做爲實際擴展點名。 如URL沒有Value,則使用缺省擴展點實現。如沒有擴展點, throw new IllegalStateException("Fail to get extension"); 在擴展點實現調用該方法,並返回結果。 } public <有@Adaptive註解的接口方法>(<方法參數>) { throw new UnsupportedOperationException("is not adaptive method!"); } }
規則以下:
@Adaptive
註解指定的Extension名;動態注入的代碼以下:
private T injectExtension(T instance) { try { if (objectFactory != null) { for (Method method : instance.getClass().getMethods()) { if (method.getName().startsWith("set") && method.getParameterTypes().length == 1 && Modifier.isPublic(method.getModifiers())) { Class<?> pt = method.getParameterTypes()[0]; try { String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : ""; Object object = objectFactory.getExtension(pt, property); if (object != null) { method.invoke(instance, object); } } catch (Exception e) { logger.error("fail to inject via method " + method.getName() + " of interface " + type.getName() + ": " + e.getMessage(), e); } } } } } catch (Exception e) { logger.error(e.getMessage(), e); } return instance; }
從上面能夠看到,進行注入的條件以下:
咱們知道一個接口的實現者可能有多個,此時到底注入哪個呢?
@Adaptive 的 dubbo spi 擴展機制,它獲取設配類不在經過前面過程生成設配類 java 源代碼, 而是在讀取擴展文件的時候遇到實現類打了註解@Adaptive 就把這個類做爲設配類緩存在 ExtensionLoader 中,調用是直接返回。使用時首先經過ExtensionLoader生成了XXX(例如Protocol)的Adaptive,以在運行期經過動態決策委託實體對象處理。此時採起的策略是,並不去注入一個具體的實現者,而是注入一個動態生成的實現者,這個動態生成的實現者的邏輯是肯定的,可以根據不一樣的參數來使用不一樣的實現者實現相應的方法。這個動態生成的實現者的class就是ExtensionLoader的Class<?> cachedAdaptiveClass
方法的參數必須是接口,而且是ExtensionLoader可以獲取其擴展類。
繼續看實現:
@SuppressWarnings("unchecked") private T createAdaptiveExtension() { try { return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e); } }
會調用:
private Class<?> getAdaptiveExtensionClass() { getExtensionClasses(); if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } return cachedAdaptiveClass = createAdaptiveExtensionClass(); }
繼續調用:
private Class<?> createAdaptiveExtensionClass() { String code = createAdaptiveExtensionClassCode(); ClassLoader classLoader = findClassLoader(); com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); return compiler.compile(code, classLoader); }
繼續:
private String createAdaptiveExtensionClassCode() { StringBuilder codeBuidler = new StringBuilder(); Method[] methods = type.getMethods(); boolean hasAdaptiveAnnotation = false; for(Method m : methods) { if(m.isAnnotationPresent(Adaptive.class)) { hasAdaptiveAnnotation = true; break; } } // 徹底沒有Adaptive方法,則不須要生成Adaptive類 if(! hasAdaptiveAnnotation) throw new IllegalStateException("No adaptive method on extension " + type.getName() + ", refuse to create the adaptive class!"); codeBuidler.append("package " + type.getPackage().getName() + ";"); codeBuidler.append("\nimport " + ExtensionLoader.class.getName() + ";"); codeBuidler.append("\npublic class " + type.getSimpleName() + "$Adpative" + " implements " + type.getCanonicalName() + " {"); for (Method method : methods) { Class<?> rt = method.getReturnType(); Class<?>[] pts = method.getParameterTypes(); Class<?>[] ets = method.getExceptionTypes(); Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class); StringBuilder code = new StringBuilder(512); if (adaptiveAnnotation == null) { code.append("throw new UnsupportedOperationException(\"method ") .append(method.toString()).append(" of interface ") .append(type.getName()).append(" is not adaptive method!\");"); } else { int urlTypeIndex = -1; for (int i = 0; i < pts.length; ++i) { if (pts[i].equals(URL.class)) { urlTypeIndex = i; break; } } // 有類型爲URL的參數 if (urlTypeIndex != -1) { // Null Point check String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"url == null\");", urlTypeIndex); code.append(s); s = String.format("\n%s url = arg%d;", URL.class.getName(), urlTypeIndex); code.append(s); } // 參數沒有URL類型 else { String attribMethod = null; // 找到參數的URL屬性 LBL_PTS: for (int i = 0; i < pts.length; ++i) { Method[] ms = pts[i].getMethods(); for (Method m : ms) { String name = m.getName(); if ((name.startsWith("get") || name.length() > 3) && Modifier.isPublic(m.getModifiers()) && !Modifier.isStatic(m.getModifiers()) && m.getParameterTypes().length == 0 && m.getReturnType() == URL.class) { urlTypeIndex = i; attribMethod = name; break LBL_PTS; } } } if(attribMethod == null) { throw new IllegalStateException("fail to create adative class for interface " + type.getName() + ": not found url parameter or url attribute in parameters of method " + method.getName()); } // Null point check String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"%s argument == null\");", urlTypeIndex, pts[urlTypeIndex].getName()); code.append(s); s = String.format("\nif (arg%d.%s() == null) throw new IllegalArgumentException(\"%s argument %s() == null\");", urlTypeIndex, attribMethod, pts[urlTypeIndex].getName(), attribMethod); code.append(s); s = String.format("%s url = arg%d.%s();",URL.class.getName(), urlTypeIndex, attribMethod); code.append(s); } String[] value = adaptiveAnnotation.value(); // 沒有設置Key,則使用「擴展點接口名的點分隔 做爲Key if(value.length == 0) { char[] charArray = type.getSimpleName().toCharArray(); StringBuilder sb = new StringBuilder(128); for (int i = 0; i < charArray.length; i++) { if(Character.isUpperCase(charArray[i])) { if(i != 0) { sb.append("."); } sb.append(Character.toLowerCase(charArray[i])); } else { sb.append(charArray[i]); } } value = new String[] {sb.toString()}; } boolean hasInvocation = false; for (int i = 0; i < pts.length; ++i) { if (pts[i].getName().equals("com.alibaba.dubbo.rpc.Invocation")) { // Null Point check String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"invocation == null\");", i); code.append(s); s = String.format("\nString methodName = arg%d.getMethodName();", i); code.append(s); hasInvocation = true; break; } } String defaultExtName = cachedDefaultName; String getNameCode = null; for (int i = value.length - 1; i >= 0; --i) { if(i == value.length - 1) { if(null != defaultExtName) { if(!"protocol".equals(value[i])) if (hasInvocation) getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); else getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName); else getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName); } else { if(!"protocol".equals(value[i])) if (hasInvocation) getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); else getNameCode = String.format("url.getParameter(\"%s\")", value[i]); else getNameCode = "url.getProtocol()"; } } else { if(!"protocol".equals(value[i])) if (hasInvocation) getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); else getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode); else getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode); } } code.append("\nString extName = ").append(getNameCode).append(";"); // check extName == null? String s = String.format("\nif(extName == null) " + "throw new IllegalStateException(\"Fail to get extension(%s) name from url(\" + url.toString() + \") use keys(%s)\");", type.getName(), Arrays.toString(value)); code.append(s); s = String.format("\n%s extension = (%<s)%s.getExtensionLoader(%s.class).getExtension(extName);", type.getName(), ExtensionLoader.class.getSimpleName(), type.getName()); code.append(s); // return statement if (!rt.equals(void.class)) { code.append("\nreturn "); } s = String.format("extension.%s(", method.getName()); code.append(s); for (int i = 0; i < pts.length; i++) { if (i != 0) code.append(", "); code.append("arg").append(i); } code.append(");"); } codeBuidler.append("\npublic " + rt.getCanonicalName() + " " + method.getName() + "("); for (int i = 0; i < pts.length; i ++) { if (i > 0) { codeBuidler.append(", "); } codeBuidler.append(pts[i].getCanonicalName()); codeBuidler.append(" "); codeBuidler.append("arg" + i); } codeBuidler.append(")"); if (ets.length > 0) { codeBuidler.append(" throws "); for (int i = 0; i < ets.length; i ++) { if (i > 0) { codeBuidler.append(", "); } codeBuidler.append(ets[i].getCanonicalName()); } } codeBuidler.append(" {"); codeBuidler.append(code.toString()); codeBuidler.append("\n}"); } codeBuidler.append("\n}"); if (logger.isDebugEnabled()) { logger.debug(codeBuidler.toString()); } return codeBuidler.toString(); }
以Protocol爲例,動態生成一個類:
package com.jason.dubbo; import com.alibaba.dubbo.common.extension.ExtensionLoader; public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol { public void destroy() { throw new UnsupportedOperationException( "method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!"); } public int getDefaultPort() { throw new UnsupportedOperationException( "method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!"); } public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException { if (arg1 == null) throw new IllegalArgumentException("url == null"); com.alibaba.dubbo.common.URL url = arg1; String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol()); if (extName == null) throw new IllegalStateException( "Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])"); com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName); return extension.refer(arg0, arg1); } public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException { if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null"); if (arg0.getUrl() == null) throw new IllegalArgumentException( "com.alibaba.dubbo.rpc.Invoker argument getUrl() == null"); com.alibaba.dubbo.common.URL url = arg0.getUrl(); String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol()); if (extName == null) throw new IllegalStateException( "Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])"); com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName); return extension.export(arg0); } }
從上面的代碼中能夠看到,Protocol$Adpative是根據URL參數中protocol屬性的值來選擇具體的實現類的。
如值爲dubbo,則從ExtensionLoader<Protocol>中獲取dubbo對應的實例,即DubboProtocol實例
如值爲hessian,則從ExtensionLoader<Protocol>中獲取hessian對應的實例,即HessianProtocol實例
也就是說Protocol$Adpative可以根據url中的protocol屬性值動態的採用對應的實現。
對於上述獲取動態實現者即Protocol$Adpative的過程還須要補充一些細節內容:
1 要求對應的接口中的某些方法必須含有Adaptive註解,沒有Adaptive註解,則表示不須要生成動態類
2 對於接口的方法中不含Adaptive註解的,所有是不可調用的,如上述的destroy()方法
3 含有Adaptive註解的方法必須含有URL類型的參數,或者可以獲取到URL,分別如上述的refer方法和export方法
4 從URL中根據什麼參數來獲取實現者信息呢?以Protocol爲例,參數就爲"protocol",默認是接口簡單名稱首字母小寫或者接口中指定的默認實現,
包裝類必須有一個參數爲spi接口類型的構造函數,不然不能正常工做。判斷warpper的標準是class有沒有一個參數爲接口類型的構造參數。Wrapper能夠有多個,會被按順序依次覆蓋,假設spi定義以下:
A=a.b.c
B=a.b.wrapper1
C=a.b.wrapper2
wrapper的最終結構則爲B-C-A
基本上將dubbo的擴展點加載機制學習了一遍,有幾點可能須要注意的地方:
ExtensionLoader
實例只負責加載一個特定擴展點實現ExtensionLoader
實例@Adaptive
註解要麼註釋在擴展點@SPI
的方法上,要麼註釋在其實現類的類定義上@Adaptive
註解註釋在@SPI
接口的方法上,那麼原則上該接口全部方法都應該加@Adaptive
註解(自動生成的實現中默認爲註解的方法拋異常)@Activate
註解)ConcurrentHashSet
存儲)
參考文獻:
一、參考:http://blog.csdn.net/jdluojing/article/details/44947221
http://www.tuicool.com/articles/FR7NnyQ Dubbo源碼學習之ExtentionLoader
http://blog.csdn.net/jdluojing/article/details/44947221