您好,登錄后才能下訂單哦!
這篇文章主要介紹“如何理解Dubbo的SPI自適應(yīng)”,在日常操作中,相信很多人在如何理解Dubbo的SPI自適應(yīng)問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”如何理解Dubbo的SPI自適應(yīng)”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
先定義一個(gè)SPI接口(被@SPI標(biāo)注):
import org.apache.dubbo.common.URL; import org.apache.dubbo.common.extension.Adaptive; import org.apache.dubbo.common.extension.SPI; @SPI public interface SpiIf { @Adaptive void test1(URL url); @Adaptive void test2(ObjHasUrl ohu); void test3(URL url); void test4(String name); }
這里的ObjHasUrl是一個(gè)內(nèi)部有URL屬性的對(duì)象,為什么要有URL屬性的原因下面會(huì)說(shuō)到。
下一步,定義兩個(gè)實(shí)現(xiàn)類(lèi)Spi1和Spi2:
public class Spi1 implements SpiIf { @Override public void test1(URL url) { System.out.println("This is Spi1:test1"); } @Override public void test2(ObjHasUrl ohu) { System.out.println("This is Spi1:test2"); } @Override public void test3(URL url) { System.out.println("This is Spi1:test3"); } @Override public void test4(String name) { System.out.println("This is Spi1:test4"); } } public class Spi2 implements SpiIf { @Override public void test1(URL url) { System.out.println("This is Spi2:test1"); } @Override public void test2(ObjHasUrl ohu) { System.out.println("This is Spi2:test2"); } @Override public void test3(URL url) { System.out.println("This is Spi2:test3"); } @Override public void test4(String name) { System.out.println("This is Spi2:test4"); } }
最后一步,定義一個(gè)Runner測(cè)試啟動(dòng)器:
public class Runner { public static void main(String[] args) { URL url = new URL("dubbo", "123", 999); url = url.addParameter("spi.if", "S2"); //設(shè)置url值,來(lái)獲取 SpiTest的自適應(yīng)擴(kuò)展 S2。 SpiIf spiIf = ExtensionLoader.getExtensionLoader(SpiIf.class).getAdaptiveExtension(); spiIf.test1(url); ObjHasUrl ohu = new ObjHasUrl(url); spiIf.test2(ohu); url = url.addParameter("spi.if", "S1"); SpiIf spiIf2 = ExtensionLoader.getExtensionLoader(SpiIf.class).getAdaptiveExtension(); spiIf.test2(ohu); } }
以上類(lèi)可以直接拷貝到自己的本地工程DEBUG用。
當(dāng)我們啟功Runner的時(shí)候,第一步先看getExtensionLoader方法,這里開(kāi)始進(jìn)入Dubbo的代碼:
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { if (type == null) { throw new IllegalArgumentException("Extension type == null"); } //驗(yàn)證是否是接口 if (!type.isInterface()) { throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!"); } //驗(yàn)證是否有SPI注解 if (!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type (" + type + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!"); } 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; }
這一步主要是獲取ExtensionLoader對(duì)象,主要是對(duì)接口類(lèi)做一些驗(yàn)證,確認(rèn)是擴(kuò)展點(diǎn)(有SPI注解)。
第二步,進(jìn)入ExtensionLoader的getAdaptiveExtension方法:
public T getAdaptiveExtension() { //先找緩存 Object instance = cachedAdaptiveInstance.get(); if (instance == null) { if (createAdaptiveInstanceError != null) { throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); } //緩存沒(méi)有,則開(kāi)始創(chuàng)建 synchronized (cachedAdaptiveInstance) { instance = cachedAdaptiveInstance.get(); if (instance == null) { try { instance = createAdaptiveExtension(); cachedAdaptiveInstance.set(instance); } catch (Throwable t) { createAdaptiveInstanceError = t; throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t); } } } } return (T) instance; }
這一步主要是獲取接口的實(shí)現(xiàn)對(duì)象實(shí)列,繼續(xù)分析實(shí)際創(chuàng)建擴(kuò)展點(diǎn)的方法:
private T createAdaptiveExtension() { try { return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e); } }
這一步拆分成兩步,第一步:getAdaptiveExtensionClass,第二步:injectExtension
第一步:
private Class<?> getAdaptiveExtensionClass() { getExtensionClasses(); if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } return cachedAdaptiveClass = createAdaptiveExtensionClass(); } private Class<?> createAdaptiveExtensionClass() { String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate(); 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); }
重點(diǎn)來(lái)了,創(chuàng)建class的時(shí)候會(huì)生成一個(gè)code(實(shí)際是SpiIf$Adaptive->SpiIf一個(gè)實(shí)現(xiàn)類(lèi)):
import org.apache.dubbo.common.extension.ExtensionLoader; public class SpiIf$Adaptive implements SpiIf { public void test2(com.zf.server.authserver.spi.dubbospitest2.ObjHasUrl arg0) { if (arg0 == null) throw new IllegalArgumentException("com.zf.server.authserver.spi.dubbospitest2.ObjHasUrl argument == null"); if (arg0.getUrl() == null) throw new IllegalArgumentException("com.zf.server.authserver.spi.dubbospitest2.ObjHasUrl argument getUrl() == null"); org.apache.dubbo.common.URL url = arg0.getUrl(); String extName = url.getParameter("spi.if"); if(extName == null) throw new IllegalStateException("Failed to get extension (com.zf.server.authserver.spi.dubbospitest2.SpiIf) name from url (" + url.toString() + ") use keys([spi.if])"); com.zf.server.authserver.spi.dubbospitest2.SpiIf extension = (com.zf.server.authserver.spi.dubbospitest2.SpiIf)ExtensionLoader.getExtensionLoader(com.zf.server.authserver.spi.dubbospitest2.SpiIf.class).getExtension(extName); extension.test2(arg0); } public void test1(org.apache.dubbo.common.URL arg0) { if (arg0 == null) throw new IllegalArgumentException("url == null"); org.apache.dubbo.common.URL url = arg0; String extName = url.getParameter("spi.if"); if(extName == null) throw new IllegalStateException("Failed to get extension (com.zf.server.authserver.spi.dubbospitest2.SpiIf) name from url (" + url.toString() + ") use keys([spi.if])"); com.zf.server.authserver.spi.dubbospitest2.SpiIf extension = (com.zf.server.authserver.spi.dubbospitest2.SpiIf)ExtensionLoader.getExtensionLoader(com.zf.server.authserver.spi.dubbospitest2.SpiIf.class).getExtension(extName); extension.test1(arg0); } public void test3(org.apache.dubbo.common.URL arg0) { throw new UnsupportedOperationException("The method public abstract void com.zf.server.authserver.spi.dubbospitest2.SpiIf.test3(org.apache.dubbo.common.URL) of interface com.zf.server.authserver.spi.dubbospitest2.SpiIf is not adaptive method!"); } public void test4(java.lang.String arg0) { throw new UnsupportedOperationException("The method public abstract void com.zf.server.authserver.spi.dubbospitest2.SpiIf.test4(java.lang.String) of interface com.zf.server.authserver.spi.dubbospitest2.SpiIf is not adaptive method!"); } }
仔細(xì)一看,其實(shí)就是為我們的接口生成了一個(gè)實(shí)現(xiàn)類(lèi)。然后為Adaptive注解標(biāo)注的方法生成了實(shí)際的內(nèi)容(就是根據(jù)URL參數(shù)來(lái)獲取實(shí)際的擴(kuò)展類(lèi)),這也解釋的Adaptive注解的實(shí)際作用。
還有一點(diǎn)需要注意:test1提供的是URL的參數(shù),test2提供的是包含URL屬性的對(duì)象。它們的共同點(diǎn)就是都包含了一個(gè)URL,如果不提供會(huì)提示沒(méi)有URL異常。具體原因可以自行分析以下方法:
new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
最終在加載生成的實(shí)現(xiàn)類(lèi)。
第二步:injectExtension
private T injectExtension(T instance) { if (objectFactory == null) { return instance; } try { for (Method method : instance.getClass().getMethods()) { if (!isSetter(method)) { continue; } 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; }
而這一步干啥呢?簡(jiǎn)單說(shuō)就是循環(huán)set方法,如果參數(shù)類(lèi)型也是一個(gè)自適應(yīng)擴(kuò)展點(diǎn)的話,繼續(xù)上面的步驟拿到擴(kuò)展點(diǎn)對(duì)象并反射注入,實(shí)現(xiàn)了Dubbo版的依賴(lài)注入。
至此,返回最終生成的對(duì)象-> SpiIf$Adaptive的實(shí)例并緩存在cachedAdaptiveInstance中,在Runner中就會(huì)根據(jù)url對(duì)應(yīng)的參數(shù)值來(lái)獲取對(duì)應(yīng)的擴(kuò)展類(lèi)。
總結(jié):
1、自適應(yīng)擴(kuò)展接口需要 SPI注解,方法需要Adaptive注解,Adaptive方法需要URL參數(shù)或者是有URL屬性的對(duì)象參數(shù);
2、最終會(huì)返回接口實(shí)現(xiàn)類(lèi)對(duì)象 SpiIf$Adaptive,里面封裝了根據(jù)url參數(shù)來(lái)獲取擴(kuò)展對(duì)象的方法;
到此,關(guān)于“如何理解Dubbo的SPI自適應(yīng)”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。