輔助連接html
* [Dubbo系列之 (一)SPI擴展 ] java
* [Dubbo系列之 (二)Registry註冊中心-註冊(1)] apache
* [Dubbo系列之 (二)Registry註冊中心-註冊(2)]編程
a、對於 @SPI,Dubbo默認的特性擴展接口,都必須打上這個@SPI,標識這是個Dubbo擴展點。若是本身須要新增dubbo的擴展點咱們就須要新增接口,而且這個接口必須標註@SPI.緩存
b、@SPI能夠填入一個值,這個值表明某個擴展點的名稱,通常做爲默認擴展點。併發
c、@Activate,這個註解能夠打在方法上或者類上,主要的做用是自動的激活某個擴展點。app
d、@Adaptive 註解爲自適應註解,該註解能夠標註在方法上或者類上。這個註解特別有用,當它標註在類上時,說明該類是AdaptiveExtension擴展,若是標註在方法上,說明該方法能夠根據參數自適應的返回值。須要注意的是該方法的參數必須有一個是URL類型或者方法參數對象內部必須有一個URL類型參數。它是經過這個URL所帶的參數進行自適應返回。當@Adaptive標註在方法上時,dubbo的spi擴展機制會動態生成一個XXXX$Adaptive擴展類,而後實現被@Adaptive標註的方法。內部的實現是經過ExtensionLoader.getActivateExtension 和傳入的URL 來返回具體哪一個擴展點實現。框架
a、該方法就是咱們對dubbo 擴展點的入口,他跟java的spi的ServiceLoader功能是同樣的,都是加載某個擴展點的全部擴展具體實現。函數
b、ExtensionLoader 查找具體擴展點實現,是去查找類路徑下 META-INF/dubbo, META-INF/dubbo/internal,META-INF/services目錄下的擴展點接口名相同的文件。並加載文件內的具體擴展點。工具
c、入口是靜態方法ExtensionLoader.getExtensionLoader(),返回某個擴展點的ExtensionLoader<?>的具體事例
d、ExtensionLoader<?>.getAdaptiveExtension 是獲得自適應的擴展點類,若是某個類打上@Adaptive,則返回該類,不然系統經過javassit建立一個。
e、ExtensionLoader<?>.getActivateExtension(URL url,key) 能夠根據URL.get(key)的值(該值就是擴展點名)獲得一個激活的擴展點。註解 @Activate 標註的爲默認的激活擴展點,能夠經過-default來不激活默認擴展點。
f、ExtensionLoader<?>.getExtension 經過name 獲得想要的擴展點,而且若是有Wrapper類型擴展點,會對其進行包裹返回。
g、當一個具體的擴展點的構造函數的參數是其擴展點接口類型,即構造函數是TypeImpl(IType type) && TypeImpl implements IType 時,能夠稱這種擴展點爲Wraper擴展點。那麼所返回的非Wraper擴展點都會被包裹成Wraper類型,若是有多個Wraper的擴展點,那麼會一層一層的包裹,可是誰包裹誰,不固定。
h、當一個具體的擴展點的實現有其setter方法且沒有被@DisableInject標註和注入類型不是原始類型(long,int,short,String,BigDecimal等)時,該setter方法會被調用,注入其須要的Object,而Object的實例原來就是經過ExtensionFactory工廠。
a、首先ExtensionFactory 這個也是SPI的擴展點,它也是經過ExtensionLoader來加載的,他加載的時機是某個具體其餘擴展點經過ExtensionLoader.getExtensionLoader()加載時,內部會調用ExtensionLoader私有構造函數,在這個構造函數內部來加載這個工廠,而且加載的這個工廠是自適應工程實例。
b、ExtensionLoader獲取具體擴展點的來源就是經過ExtensionFactory擴展點的自適應擴展點AdaptiveExtensionFactory。
c、AdaptiveExtensionFactory 的實現能夠知道擴展點的實例注入來源,包括Spring 容器(經過SpringExtensionFactory)和 SpiExtensionFactory。
接着咱們對上面的特性,進行一輪測試,驗證其結果。
新建一個擴展接口
package org.apache.dubbo.mytest; @SPI("first") public interface InvokerProtocol { @Adaptive("getInvoker") Invoker getInvoker(String name, URL url); }
默認的擴展點爲first,而且getInvoker方法被@Adaptive標註。接着咱們在META-INF/services 下新建一個文件名爲org.apache.dubbo.mytest.InvokerProtocol文件,裏面填寫這個擴展點的實現,如:
first=org.apache.dubbo.mytest.protocol.FirstInvokerProtocol
@Test public void testDefaultExtension(){ ExtensionLoader<InvokerProtocol> extensionLoader = ExtensionLoader.getExtensionLoader(InvokerProtocol.class); InvokerProtocol firstInvokerProtocol = extensionLoader.getExtension("first"); Assert.check(firstInvokerProtocol instanceof FirstInvokerProtocol); //true InvokerProtocol defaultExtension = extensionLoader.getDefaultExtension(); Assert.check(defaultExtension ==firstInvokerProtocol); //true }
從上面咱們驗證了SPI("first")標註的名稱,指定爲默認的擴展實現類。接着咱們在org.apache.dubbo.mytest.InvokerProtocol文件,接着新增一個擴展點,
second=org.apache.dubbo.mytest.protocol.SecondInvokerProtocol,並把SecondInvokerProtocol類打上@Activate("second")註解。注意,FirstInvokerProtocol沒有打上@Activate註解。接着再一次測試
@Test public void testActivateExtension(){ ExtensionLoader<InvokerProtocol> extensionLoader = ExtensionLoader.getExtensionLoader(InvokerProtocol.class); URLBuilder urlBuilder = new URLBuilder(); urlBuilder.setProtocol("test"); urlBuilder.setPath("defaultInvokerProtocol"); urlBuilder.addParameter("invokerProtocol", "first"); List<InvokerProtocol> invokerProtocol = extensionLoader.getActivateExtension(urlBuilder.build(), "invokerProtocol"); Assert.check(invokerProtocol.size() == 2); //true }
從該上面例子,驗證了getActivateExtension 能夠經過URL 來激活指定的擴展實現,而且還會返回被@Activate的擴展點類,說明@Activate被標註的擴展點類被默認激活。當咱們在org.apache.dubbo.mytest.InvokerProtocol文件,接着新增二個擴展點,以下
firstwraper=org.apache.dubbo.mytest.protocol.FirstWraperInvokerProtocol
secondwraper=org.apache.dubbo.mytest.protocol.SecondWraperInvokerProtocol
這個2個擴展點是包裹擴展點,它的構造函數以下:
public FirstWraperInvokerProtocol(InvokerProtocol invokerProtocol) { this.invokerProtocol = invokerProtocol; }
這時,咱們運行test1的單測,發現斷言失敗,而且知道返回的擴展點實例被這2個wraper擴展點包裹。
@Test public void testDefaultExtension(){ ExtensionLoader<InvokerProtocol> extensionLoader = ExtensionLoader.getExtensionLoader(InvokerProtocol.class); InvokerProtocol firstInvokerProtocol = extensionLoader.getExtension("first"); InvokerProtocol defaultExtension = extensionLoader.getDefaultExtension(); }
從上面,得知firstInvokerProtocol 類型是FirstWraperInvokerProtocol 而且持有SecondWraperInvokerProtocol類型,而SecondWraperInvokerProtocol最終持有咱們的FirstInvokerProtocol.
接着測試,咱們獲取下AdaptiveExtension擴展,目前咱們只在擴展點的接口方法上打上@Adaptive,即getInvoker方法。
@Test public void testExtensionAdaptive(){ InvokerProtocol adaptiveExtension = ExtensionLoader.getExtensionLoader(InvokerProtocol.class).getAdaptiveExtension(); System.out.println(adaptiveExtension); }
能夠發現框架動態爲咱們生產了一個類名爲InvokerProtocol$Adaptive的自適應擴展點類。而且實現其getInvoker方法,方法內容爲(整理了下)。
public class InvokerProtocol$Adaptive implements InvokerProtocol { public Invoker getInvoker(String arg0, URL arg1) { if (arg1 == null) throw new IllegalArgumentException("url == null"); URL url = arg1; String extName = url.getParameter("getInvoker", "first"); if (extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.mytest.InvokerProtocol) name " + "from url (" + url.toString() + ") use keys([getInvoker])"); InvokerProtocol extension = ExtensionLoader.getExtensionLoader(InvokerProtocol.class).getExtension(extName); return extension.getInvoker(arg0, arg1); } }
咱們知道其內部經過URL參數,根據url.getParameter獲得咱們咱們須要的具體須要的擴展點名,接着ExtensionLoader.getExtension獲得具體擴展點。
接着咱們本身實現一個名爲AdaptiveInvokerProtocol的自適應的擴展點類,並把AdaptiveInvokerProtocol標註上@Adaptive,接着在org.apache.dubbo.mytest.InvokerProtocol文件下,填入:
adaptive=org.apache.dubbo.mytest.protocol.AdaptiveInvokerProtocol
再一次執行
@Test public void testExtensionAdaptive(){ InvokerProtocol adaptiveExtension = ExtensionLoader.getExtensionLoader(InvokerProtocol.class).getAdaptiveExtension(); System.out.println(adaptiveExtension); }
能夠知道,自適應擴展點變爲咱們實現的AdaptiveInvokerProtocol,框架沒有在爲咱們建立一個自適應的擴展點類。
咱們經過若是下幾個問題來跟蹤咱們的代碼。
咱們在獲取擴展點的入口爲ExtensionLoader.getExtensionLoader。在這個靜態方法裏作了@SPI註解判斷。
// 說明該接口上是否有SPI註解 private static <T> boolean withExtensionAnnotation(Class<T> type) { return type.isAnnotationPresent(SPI.class); } @SuppressWarnings("unchecked") 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 an interface!"); } if (!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type (" + type + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!"); } // 併爲每種類型的擴展建立一個具體類型的ExtensionLoader<?>的實例,並放入EXTENSION_LOADERS緩存中。 ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); if (loader == null) { EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); } return loader; }
咱們經過 getActivateExtension 獲得激活的擴展點,看下面的具體註釋:
public List<T> getActivateExtension(URL url, String[] values, String group) { List<T> activateExtensions = new ArrayList<>(); List<String> names = values == null ? new ArrayList<>(0) : asList(values); // 若是傳入的值,沒有包括-default,說明不排除框架,默認激活的擴展點 if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) { getExtensionClasses(); // 加載具體擴展點類,在加載過程當中會把打上@Activate註解的類放到緩存cachedActivates中,其中key爲擴展點名,Value爲Activate註解 for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) { String name = entry.getKey(); Object activate = entry.getValue(); String[] activateGroup, activateValue; //獲得Activate註解上的group和value. if (activate instanceof Activate) { activateGroup = ((Activate) activate).group(); activateValue = ((Activate) activate).value(); } else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) { activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group(); activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value(); } else { continue; } //判斷是否匹配,若是匹配加到activateExtensions。 if (isMatchGroup(group, activateGroup) && !names.contains(name) && !names.contains(REMOVE_VALUE_PREFIX + name) && isActive(activateValue, url)) { activateExtensions.add(getExtension(name)); } } activateExtensions.sort(ActivateComparator.COMPARATOR); } // 上面的if獲取的是知足條件默認激活的擴展點類。這裏是URL參數直接指定須要的擴展點放到loadedExtensions List<T> loadedExtensions = new ArrayList<>(); for (int i = 0; i < names.size(); i++) { String name = names.get(i); if (!name.startsWith(REMOVE_VALUE_PREFIX) && !names.contains(REMOVE_VALUE_PREFIX + name)) { if (DEFAULT_KEY.equals(name)) { if (!loadedExtensions.isEmpty()) { activateExtensions.addAll(0, loadedExtensions); loadedExtensions.clear(); } } else { loadedExtensions.add(getExtension(name)); } } } if (!loadedExtensions.isEmpty()) { activateExtensions.addAll(loadedExtensions); } // 接着返回默認的激活的擴展點+用戶指定的擴展點。 return activateExtensions; }
自適應的擴展點實現類只能有一個,咱們經過getAdaptiveExtension來獲取,內容以下:
public T getAdaptiveExtension() { //首先cachedAdaptiveInstance用來存放自適應擴展點實例的Holder Object instance = cachedAdaptiveInstance.get(); if (instance == null) { // 若是不存在 if (createAdaptiveInstanceError != null) { throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); } synchronized (cachedAdaptiveInstance) { //鎖住該Holder instance = cachedAdaptiveInstance.get();//再次獲取,應爲該方法多是併發環境下,因此鎖定雙層判斷 if (instance == null) { try { instance = createAdaptiveExtension(); //這裏建立一個自適應擴展點實例 cachedAdaptiveInstance.set(instance);// 並放入Holder中 } catch (Throwable t) { createAdaptiveInstanceError = t; throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t); } } } } return (T) instance; }
接着咱們來看下,這個createAdaptiveExtension是如何建立的。
private T createAdaptiveExtension() { try { // 步驟很是清晰,首先經過getAdaptiveExtensionClass獲得自適應的擴展點的Class,而後調用newInstance獲得一個實例 //接着調用 injectExtension方法爲這個實例注入相關須要的熟悉,經過這個實例的setter方法。 return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e); } }
private Class<?> getAdaptiveExtensionClass() { getExtensionClasses(); // 這裏仍是經過getExtensionClasses加載擴展類,以後分析 if (cachedAdaptiveClass != null) { //當調用完 getExtensionClasses方法後,若是存在自適應的擴展點類,會被賦值給cachedAdaptiveClass,那麼直接返回, return cachedAdaptiveClass; } // 若是沒有找到被@Adaptive標註的類,爲其建立一個自適應的擴展點類 return cachedAdaptiveClass = createAdaptiveExtensionClass(); }
// 建立的一個自適應的擴展點的代碼比較簡單,就是StringBuilder 建立一個java類文件內容,而後通不過dubbo提供的compiler工具進行編譯爲字節碼,經過javaassit完成。
private Class<?> createAdaptiveExtensionClass() { String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate(); System.out.println(code); ClassLoader classLoader = findClassLoader(); org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); return compiler.compile(code, classLoader); }
在經過getExtensionClasses 加載擴展點類時,會判斷這個類時否是Wraper,判斷以下:
private boolean isWrapperClass(Class<?> clazz) { try { clazz.getConstructor(type); return true; } catch (NoSuchMethodException e) { return false; } }
即構造函數的參數否爲自身擴展點,並把這些類放入一個名爲cachedWrapperClasses的緩存中。
private void cacheWrapperClass(Class<?> clazz) { if (cachedWrapperClasses == null) { cachedWrapperClasses = new ConcurrentHashSet<>(); } cachedWrapperClasses.add(clazz); }
當咱們調用ExtensionLoader<?>.getExtension 時,
public T getExtension(String name) { if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("Extension name == null"); } // 若是名稱爲true,返回默認的擴展點 if ("true".equals(name)) { return getDefaultExtension(); } //從holder中獲得名爲name的擴展點 final Holder<Object> holder = getOrCreateHolder(name); Object instance = holder.get(); if (instance == null) { synchronized (holder) { //這裏也是鎖定雙層判斷 instance = holder.get(); if (instance == null) { instance = createExtension(name); // 建立該擴展點實例,並放入該Holder中 holder.set(instance); } } } return (T) instance; }
private T createExtension(String name) { // 調用getExtensionClasses 獲得擴展點類 Class<?> clazz = getExtensionClasses().get(name); if (clazz == null) { throw findException(name); } try { T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { // 爲空,clazz.newInstance()一個實力 EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } // 爲擴展點setter注入 injectExtension(instance); // 這裏,對包裹擴展點進行for循環,而後調用wrapperClass.getConstructor(type).newInstance ,一次循環包裹對象 Set<Class<?>> wrapperClasses = cachedWrapperClasses; if (CollectionUtils.isNotEmpty(wrapperClasses)) { for (Class<?> wrapperClass : wrapperClasses) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } initExtension(instance); return instance; //接着返回被包裹的實例 } catch (Throwable t) { throw new IllegalStateException("Extension instance (name: " + name + ", class: " + type + ") couldn't be instantiated: " + t.getMessage(), t); } }
在 injectExtension方法中能夠獲得答案,這個方法被createAdaptiveExtension和createExtension調用,都是在建立完具體擴展點實例後,對其注入。
private T injectExtension(T instance) { if (objectFactory == null) { return instance; } try { for (Method method : instance.getClass().getMethods()) { if (!isSetter(method)) { continue; } /** * Check {@link DisableInject} to see if we need auto injection for this property */ if (method.getAnnotation(DisableInject.class) != null) { continue; } Class<?> pt = method.getParameterTypes()[0]; if (ReflectUtils.isPrimitives(pt)) { continue; } try { String property = getSetterProperty(method); Object object = objectFactory.getExtension(pt, property); if (object != null) { method.invoke(instance, object); } } catch (Exception e) { logger.error("Failed to inject via method " + method.getName() + " of interface " + type.getName() + ": " + e.getMessage(), e); } } } catch (Exception e) { logger.error(e.getMessage(), e); } return instance; }
在injectExtension方法中,咱們看到objectFactory.getExtension(pt, property),即從objectFactory獲得須要注入的屬性對象。objectFactory 的實例化,咱們是在ExtensionLoader私有構造函數中。
private ExtensionLoader(Class<?> type) { this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
而私有構造函數在getExtensionLoader的靜態方法上調用。而且objectFactory仍是自適應的擴展點實現。因此咱們在加載一個擴展點時,首先框架內部會實例化這個ExtensionFactory工廠。而且這個自適應工程名爲AdaptiveExtensionFactory,內容就是就是組合Spi和Spring的ExtensionFactory。
流程比較清晰,不具體的貼代碼了。簡潔流程調用以下 :
getExtensionClasses (這裏是在緩存cachedClasses取,緩存取不到下一步) ->loadExtensionClasses(主要從META-INF/services,META-INF/dubbo,META-INF/dubbo/internal文件下找具體的擴展點類)
在這過程當中把一些不一樣類型的擴展點放入到緩存中。具體看loadClass
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name, boolean overridden) throws NoSuchMethodException { // 防護性編程,看加載的具體實現類是否是擴展點接口的實現類 if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException("Error occurred when loading extension class (interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + " is not subtype of interface."); } // 若是該類被Adaptive標註,放入緩存cachedAdaptiveClass if (clazz.isAnnotationPresent(Adaptive.class)) { cacheAdaptiveClass(clazz, overridden); // 若是該類是Wrapper類型,放入緩存cachedWrapperClasses } else if (isWrapperClass(clazz)) { cacheWrapperClass(clazz); } else { //這裏判斷是否有默認構造函數, clazz.getConstructor(); if (StringUtils.isEmpty(name)) { name = findAnnotationName(clazz); if (name.length() == 0) { throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL); } } String[] names = NAME_SEPARATOR.split(name); if (ArrayUtils.isNotEmpty(names)) { cacheActivateClass(clazz, names[0]); for (String n : names) { cacheName(clazz, n); saveInExtensionClass(extensionClasses, clazz, n, overridden); } } } }
經過loadClass咱們知道,ExtensionLoader內部緩存主要包括以下三個:
cachedClasses:存放沒有被@Adaptive標註的且不是Wrapper擴展點類
cachedWrapperClasses :存放Wrapper類型的擴展點類
cachedAdaptiveClass :存放@Adaptive標註的類,沒有被@Adaptive標註時,系統自動建立一個。