一文講清楚DUBBO SPI機(jī)制六個(gè)特性
JAVA前線?
歡迎大家關(guān)注公眾號(hào)「JAVA前線」查看更多精彩分享,主要內(nèi)容包括源碼分析、實(shí)際應(yīng)用、架構(gòu)思維、職場(chǎng)分享、產(chǎn)品思考等等,同時(shí)也非常歡迎大家加我微信「java_front」一起交流學(xué)習(xí)
1 文章概述
SPI(Service Provider Interface)是一種服務(wù)發(fā)現(xiàn)機(jī)制,本質(zhì)是將接口實(shí)現(xiàn)類(lèi)全限定名配置在文件,并由服務(wù)加載器讀取配置文件加載實(shí)現(xiàn)類(lèi),這樣可以在運(yùn)行時(shí)動(dòng)態(tài)為接口替換實(shí)現(xiàn)類(lèi),通過(guò)SPI機(jī)制可以為程序提供拓展功能。
我們之前在文章《JDK SPI機(jī)制》已經(jīng)討論了JDK SPI如何實(shí)現(xiàn),在文章《SLF4J源碼角度分析阿里開(kāi)發(fā)手冊(cè)日志規(guī)約》已經(jīng)討論了JDK SPI如何應(yīng)用,本文我們分析DUBBO SPI機(jī)制,相較于JDK SPI至少進(jìn)行了以下功能擴(kuò)展:
指定具體擴(kuò)展點(diǎn) 指定默認(rèn)擴(kuò)展點(diǎn) 類(lèi)級(jí)別自適應(yīng)擴(kuò)展點(diǎn) 方法級(jí)自適應(yīng)擴(kuò)展點(diǎn) 自實(shí)現(xiàn)IOC 自實(shí)現(xiàn)AOP
2 指定具體擴(kuò)展點(diǎn)
2.1 代碼實(shí)例
第一步定義訂單接口與訂單模型
package?com.java.front.dubbo.spi.api.order;
import?org.apache.dubbo.common.extension.SPI;
@SPI
public?interface?OrderSpiService?{
????public?boolean?createOrder(OrderModel?order);
}
public?class?OrderModel?{
????private?String?userId;
????private?String?orderId;
}
第二步實(shí)現(xiàn)訂單服務(wù)
package?com.java.front.dubbo.spi.impl.order;
public?class?OrderSpiAServiceImpl?implements?OrderSpiService?{
????@Override
????public?boolean?createOrder(OrderModel?order)?{
????????System.out.println("OrderSpiAService?createOrder");
????????return?Boolean.TRUE;
????}
}
public?class?OrderSpiBServiceImpl?implements?OrderSpiService?{
????@Override
????public?boolean?createOrder(OrderModel?order)?{
????????System.out.println("OrderSpiBService?createOrder");
????????return?Boolean.TRUE;
????}
}
第三步新增配置文件
├─src
│ ├─main
│ │ └─resources
│ │ └─META-INF
│ │ ├─services
│ │ │ com.java.front.dubbo.spi.api.order.OrderSpiService
第四步新增配置文件內(nèi)容
orderSpiAService=com.java.front.dubbo.spi.impl.order.OrderSpiAServiceImpl
orderSpiBService=com.java.front.dubbo.spi.impl.order.OrderSpiBServiceImpl
第五步運(yùn)行測(cè)試代碼
public?class?OrderSpiServiceTest?{
????public?static?void?main(String[]?args)?{
????????test1();
????}
????public?static?void?test1()?{
????????ExtensionLoader?extensionLoader?=?ExtensionLoader.getExtensionLoader(OrderSpiService.class);
????????OrderSpiService?orderSpiService?=?extensionLoader.getExtension("orderSpiAService");
????????orderSpiService.createOrder(new?OrderModel());
????}
}
第六步輸出測(cè)試結(jié)果
OrderSpiAService createOrder
2.2 源碼分析

public?class?ExtensionLoader<T>?{
????private?static?final?String?SERVICES_DIRECTORY?=?"META-INF/services/";
????private?static?final?String?DUBBO_DIRECTORY?=?"META-INF/dubbo/";
????private?static?final?String?DUBBO_INTERNAL_DIRECTORY?=?DUBBO_DIRECTORY?+?"internal/";
????private?Map>?loadExtensionClasses()?{
????????//?省略代碼
????????//?KEY表示自定義名稱(chēng)、Value表示具體實(shí)現(xiàn)類(lèi)
????????//?orderSpiAService=class?com.java.front.dubbo.spi.impl.order.OrderSpiAServiceImpl
????????//?orderSpiBService=class?com.java.front.dubbo.spi.impl.order.OrderSpiBServiceImpl
????????Map>?extensionClasses?=?new?HashMap>();
????????loadDirectory(extensionClasses,?DUBBO_INTERNAL_DIRECTORY,?type.getName());
????????loadDirectory(extensionClasses,?DUBBO_INTERNAL_DIRECTORY,?type.getName().replace("org.apache",?"com.alibaba"));
????????loadDirectory(extensionClasses,?DUBBO_DIRECTORY,?type.getName());
????????loadDirectory(extensionClasses,?DUBBO_DIRECTORY,?type.getName().replace("org.apache",?"com.alibaba"));
????????loadDirectory(extensionClasses,?SERVICES_DIRECTORY,?type.getName());
????????loadDirectory(extensionClasses,?SERVICES_DIRECTORY,?type.getName().replace("org.apache",?"com.alibaba"));
????????return?extensionClasses;
????}
????private?void?loadDirectory(Map>?extensionClasses,?String?dir,?String?type) ?{
????????String?fileName?=?dir?+?type;
????????try?{
????????????Enumeration?urls;
????????????ClassLoader?classLoader?=?findClassLoader();
????????????if?(classLoader?!=?null)?{
????????????????urls?=?classLoader.getResources(fileName);
????????????}?else?{
????????????????urls?=?ClassLoader.getSystemResources(fileName);
????????????}
????????????if?(urls?!=?null)?{
????????????????//?遍歷所有文件
????????????????while?(urls.hasMoreElements())?{
????????????????????java.net.URL?resourceURL?=?urls.nextElement();
????????????????????loadResource(extensionClasses,?classLoader,?resourceURL);
????????????????}
????????????}
????????}?catch?(Throwable?t)?{
????????????logger.error("Exception?when?load?extension?class(interface:?"?+?type?+?",?description?file:?"?+?fileName?+?").",?t);
????????}
????}
????private?void?loadResource(Map>?extensionClasses,?ClassLoader?classLoader,?java.net.URL?resourceURL) ?{
????????try?{
????????????BufferedReader?reader?=?new?BufferedReader(new?InputStreamReader(resourceURL.openStream(),?"utf-8"));
????????????try?{
????????????????//?讀取文件每一行
????????????????String?line;
????????????????while?((line?=?reader.readLine())?!=?null)?{
????????????????????final?int?ci?=?line.indexOf('#');
????????????????????if?(ci?>=?0)?{
????????????????????????line?=?line.substring(0,?ci);
????????????????????}
????????????????????line?=?line.trim();
????????????????????if?(line.length()?>?0)?{
????????????????????????try?{
????????????????????????????String?name?=?null;
????????????????????????????//?等號(hào)作為分隔符
????????????????????????????int?i?=?line.indexOf('=');
????????????????????????????if?(i?>?0)?{
????????????????????????????????name?=?line.substring(0,?i).trim();
????????????????????????????????line?=?line.substring(i?+?1).trim();
????????????????????????????}
????????????????????????????if?(line.length()?>?0)?{
????????????????????????????????//?加載實(shí)現(xiàn)類(lèi)
????????????????????????????????loadClass(extensionClasses,?resourceURL,?Class.forName(line,?true,?classLoader),?name);
????????????????????????????}
????????????????????????}?catch?(Throwable?t)?{
????????????????????????????IllegalStateException?e?=?new?IllegalStateException("Failed?to?load?extension?class(interface:?"?+?type?+?",?class?line:?"?+?line?+?")?in?"?+?resourceURL?+?",?cause:?"?+?t.getMessage(),?t);
????????????????????????????exceptions.put(line,?e);
????????????????????????}
????????????????????}
????????????????}
????????????}?finally?{
????????????????reader.close();
????????????}
????????}?catch?(Throwable?t)?{
????????????logger.error("Exception?when?load?extension?class(interface:?"?+?type?+?",?class?file:?"?+?resourceURL?+?")?in?"?+?resourceURL,?t);
????????}
????}
}
classes.get(name)根據(jù)輸入名稱(chēng)獲取擴(kuò)展點(diǎn)
public?class?ExtensionLoader<T>?{
????private?T?createExtension(String?name)?{
????????//?根據(jù)name獲取對(duì)應(yīng)實(shí)現(xiàn)類(lèi)
????????Class>?clazz?=?getExtensionClasses().get(name);
????????if?(clazz?==?null)?{
????????????throw?findException(name);
????????}
????????try?{
????????????//?獲取實(shí)現(xiàn)類(lèi)對(duì)象
????????????T?instance?=?(T)?EXTENSION_INSTANCES.get(clazz);
????????????if?(instance?==?null)?{
????????????????EXTENSION_INSTANCES.putIfAbsent(clazz,?clazz.newInstance());
????????????????instance?=?(T)?EXTENSION_INSTANCES.get(clazz);
????????????}
????????????//?省略代碼
????????}?catch?(Throwable?t)?{
????????????throw?new?IllegalStateException("Extension?instance(name:?"?+?name?+?",?class:?"?+?type?+?")??could?not?be?instantiated:?"?+?t.getMessage(),?t);
????????}
????????return?instance;
????}
}
3 指定默認(rèn)擴(kuò)展點(diǎn)
3.1 代碼實(shí)例
第一步修改訂單接口
package?com.java.front.dubbo.spi.api.order;
import?org.apache.dubbo.common.extension.SPI;
@SPI("orderSpiBService")
public?interface?OrderSpiService?{
????public?boolean?createOrder(OrderModel?order);
}
第二步運(yùn)行測(cè)試代碼
public?class?OrderSpiServiceTest?{
????public?static?void?main(String[]?args)?{
????????test2();
????}
????public?static?void?test2()?{
????????ExtensionLoader?extensionLoader?=?ExtensionLoader.getExtensionLoader(OrderSpiService.class);
????????OrderSpiService?orderSpiService?=?extensionLoader.getDefaultExtension();
????????orderSpiService.createOrder(new?OrderModel());
????}
}
第三步輸出測(cè)試結(jié)果
OrderSpiBService createOrder
3.2 源碼分析
源碼流程如下圖

getDefaultExtension方法獲取默認(rèn)擴(kuò)展點(diǎn)
public?class?ExtensionLoader<T>?{
????public?T?getDefaultExtension()?{
????????//?加載實(shí)現(xiàn)類(lèi)并設(shè)置cachedDefaultName
????????getExtensionClasses();
????????if?(null?==?cachedDefaultName?||?cachedDefaultName.length()?==?0?||?"true".equals(cachedDefaultName))?{
????????????return?null;
????????}
????????//?根據(jù)默認(rèn)名稱(chēng)獲取擴(kuò)展點(diǎn)
????????return?getExtension(cachedDefaultName);
????}
}
loadExtensionClasses方法設(shè)置默認(rèn)擴(kuò)展點(diǎn)名
public?class?ExtensionLoader<T>?{
????private?Map>?loadExtensionClasses()?{
????????//?一個(gè)接口只允許有一個(gè)默認(rèn)擴(kuò)展點(diǎn)
????????//?@SPI("orderSpiBService")表示設(shè)置orderSpiBService作為默認(rèn)擴(kuò)展點(diǎn)
????????if?(defaultAnnotation?!=?null)?{
????????????String?value?=?defaultAnnotation.value();
????????????if?((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];
????????????????}
????????????}
????????}
????????//?KEY表示自定義名稱(chēng)、Value表示具體實(shí)現(xiàn)類(lèi)
????????//?orderSpiAService=class?com.java.front.dubbo.spi.impl.order.OrderSpiAServiceImpl
????????//?orderSpiBService=class?com.java.front.dubbo.spi.impl.order.OrderSpiBServiceImpl
????????Map>?extensionClasses?=?new?HashMap>();
????????loadDirectory(extensionClasses,?DUBBO_INTERNAL_DIRECTORY,?type.getName());
????????loadDirectory(extensionClasses,?DUBBO_INTERNAL_DIRECTORY,?type.getName().replace("org.apache",?"com.alibaba"));
????????loadDirectory(extensionClasses,?DUBBO_DIRECTORY,?type.getName());
????????loadDirectory(extensionClasses,?DUBBO_DIRECTORY,?type.getName().replace("org.apache",?"com.alibaba"));
????????loadDirectory(extensionClasses,?SERVICES_DIRECTORY,?type.getName());
????????loadDirectory(extensionClasses,?SERVICES_DIRECTORY,?type.getName().replace("org.apache",?"com.alibaba"));
????????return?extensionClasses;
????}
}
4 類(lèi)級(jí)別自適應(yīng)擴(kuò)展點(diǎn)
4.1 代碼實(shí)例
第一步新增訂單自適應(yīng)實(shí)現(xiàn)類(lèi)
package?com.java.front.dubbo.spi.impl.order;
import?org.apache.dubbo.common.extension.Adaptive;
@Adaptive
public?class?OrderSpiAdaptiveServiceImpl?implements?OrderSpiService?{
????@Override
????public?boolean?createOrder(OrderModel?order)?{
????????System.out.println("OrderSpiAdaptiveService?createOrder");
????????return?Boolean.TRUE;
????}
}
第二步配置文件新增
orderAdaptiveService=com.java.front.dubbo.spi.impl.order.OrderSpiAdaptiveServiceImpl
第三步運(yùn)行測(cè)試代碼
public?class?OrderSpiServiceTest?{
????public?static?void?main(String[]?args)?{
????????test3();
????}
????public?static?void?test3()?{
????????ExtensionLoader?extensionLoader?=?ExtensionLoader.getExtensionLoader(OrderSpiService.class);
????????OrderSpiService?orderSpiService?=?extensionLoader.getAdaptiveExtension();
????????orderSpiService.createOrder(new?OrderModel());
????}
}
第四步輸出測(cè)試結(jié)果
OrderSpiAdaptiveService createOrder
4.2 源碼分析
源碼流程如下圖

getAdaptiveExtensionClass方法判斷是否存在類(lèi)級(jí)別自適應(yīng)擴(kuò)展點(diǎn),如果存在則直接返回
public?class?ExtensionLoader<T>?{
????private?Class>?getAdaptiveExtensionClass()?{
????????//?加載擴(kuò)展點(diǎn)并設(shè)置cachedAdaptiveClass
????????getExtensionClasses();
????????//?存在類(lèi)級(jí)別自適應(yīng)擴(kuò)展點(diǎn)則直接返回
????????if?(cachedAdaptiveClass?!=?null)?{
????????????return?cachedAdaptiveClass;
????????}
????????//?不存在類(lèi)級(jí)別自適應(yīng)擴(kuò)展點(diǎn)則動(dòng)態(tài)創(chuàng)建
????????return?cachedAdaptiveClass?=?createAdaptiveExtensionClass();
????}
}
loadClass方法發(fā)現(xiàn)存在類(lèi)級(jí)別自適應(yīng)擴(kuò)展點(diǎn)則設(shè)置cachedAdaptiveClass
public?class?ExtensionLoader<T>?{
????private?void?loadClass(Map>?extensionClasses,?java.net.URL?resourceURL,?Class>?clazz,?String?name) ?throws?NoSuchMethodException?{
????????//?clazz是否為接口實(shí)現(xiàn)類(lèi)
????????if?(!type.isAssignableFrom(clazz))?{
????????????throw?new?IllegalStateException("Error?when?load?extension?class(interface:?"?+?type?+?",?class?line:?"?+?clazz.getName()?+?"),?class?"?+?clazz.getName()?+?"is?not?subtype?of?interface.");
????????}
????????//?存在類(lèi)級(jí)別自適應(yīng)擴(kuò)展點(diǎn)則設(shè)置cachedAdaptiveClass
????????if?(clazz.isAnnotationPresent(Adaptive.class))?{
????????????if?(cachedAdaptiveClass?==?null)?{
????????????????cachedAdaptiveClass?=?clazz;
????????????}
????????????//?一個(gè)接口只允許有一個(gè)類(lèi)級(jí)別自適應(yīng)擴(kuò)展點(diǎn)
????????????else?if?(!cachedAdaptiveClass.equals(clazz))?{
????????????????throw?new?IllegalStateException("More?than?1?adaptive?class?found:?"?+?cachedAdaptiveClass.getClass().getName()?+?",?"?+?clazz.getClass().getName());
????????????}
????????}
????????//?省略代碼
????}
}
5 方法級(jí)自適應(yīng)擴(kuò)展點(diǎn)
5.1 代碼實(shí)例
第一步新建庫(kù)存接口與庫(kù)存實(shí)體,使用方法級(jí)別自適應(yīng)擴(kuò)展點(diǎn)需要滿(mǎn)足以下任意一個(gè)條件:
方法必須包含URL輸入?yún)?shù) 方法必須包含get方法且返回值為URL
package?com.java.front.dubbo.spi.api.stock;
import?org.apache.dubbo.common.URL;
@SPI("stockA")
public?interface?StockSpiService?{
????@Adaptive("bizType")
????public?boolean?reduceStock(String?skuId,?URL?url);
????@Adaptive("bizType")
????public?boolean?reduceStock2(StockReduceModel?stockReduceModel);
}
public?class?StockReduceModel?{
????private?String?skuId;
????private?URL?url;
????public?StockReduceModel(String?skuId,?URL?url)?{
????????this.skuId?=?skuId;
????????this.url?=?url;
????}
????public?String?getSkuId()?{
????????return?skuId;
????}
????public?void?setSkuId(String?skuId)?{
????????this.skuId?=?skuId;
????}
????public?URL?getUrl()?{
????????return?url;
????}
????public?void?setUrl(URL?url)?{
????????this.url?=?url;
????}
}
第二步實(shí)現(xiàn)庫(kù)存服務(wù)
package?com.java.front.dubbo.spi.impl.stock;
import?org.apache.dubbo.common.URL;
public?class?StockSpiAServiceImpl?implements?StockSpiService?{
????@Override
????public?boolean?reduceStock1(String?skuId,?URL?url)?{
????????System.out.println("StockSpiAService?reduceStock1?skuId="?+?skuId);
????????return?Boolean.TRUE;
????}
????@Override
????public?boolean?reduceStock2(StockReduceModel?stockReduceModel)?{
????????System.out.println("StockSpiAService?reduceStock2?stockReduceModel="?+?stockReduceModel);
????????return?Boolean.TRUE;
????}
}
public?class?StockSpiBServiceImpl?implements?StockSpiService?{
????@Override
????public?boolean?reduceStock1(String?skuId,?URL?url)?{
????????System.out.println("StockSpiBService?reduceStock1?skuId="?+?skuId);
????????return?Boolean.TRUE;
????}
????@Override
????public?boolean?reduceStock2(StockReduceModel?stockReduceModel)?{
????????System.out.println("StockSpiBService?reduceStock2?stockReduceModel="?+?stockReduceModel);
????????return?Boolean.TRUE;
????}
}
第三步新增配置文件
├─src
│ ├─main
│ │ └─resources
│ │ └─META-INF
│ │ ├─services
│ │ │ com.java.front.dubbo.spi.api.stock.StockSpiService
第四步新增配置文件內(nèi)容
stockA=com.java.front.dubbo.spi.impl.stock.StockSpiAServiceImpl
stockB=com.java.front.dubbo.spi.impl.stock.StockSpiBServiceImpl
第五步運(yùn)行測(cè)試代碼
public?class?StockSpiServiceTest?{
????public?static?void?main(String[]?args)?{
????????test1();
????????test2();
????}
????public?static?void?test1()?{
????????ExtensionLoader?extensionLoader?=?ExtensionLoader.getExtensionLoader(StockSpiService.class);
????????StockSpiService?adaptiveService?=?extensionLoader.getAdaptiveExtension();
????????Map?map?=?new?HashMap<>();
????????map.put("bizType",?"stockB");
????????URL?url?=?new?URL(StringUtils.EMPTY,?StringUtils.EMPTY,?1,?map);
????????adaptiveService.reduceStock1("skuId_111",?url);
????}
????public?static?void?test2()?{
????????ExtensionLoader?extensionLoader?=?ExtensionLoader.getExtensionLoader(StockSpiService.class);
????????StockSpiService?adaptiveService?=?extensionLoader.getAdaptiveExtension();
????????Map?map?=?new?HashMap<>();
????????map.put("bizType",?"stockB");
????????URL?url?=?new?URL(StringUtils.EMPTY,?StringUtils.EMPTY,?1,?map);
????????StockReduceModel?stockReduceModel?=?new?StockReduceModel("skuId_111",?url);
????????adaptiveService.reduceStock2(stockReduceModel);
????}
}
第六步輸出測(cè)試結(jié)果
StockSpiBService reduceStock1 skuId=skuId_111
StockSpiBService reduceStock2 stockReduceModel=StockReduceModel(skuId=skuId_111, url=?bizType=stockB)
5.2 源碼分析
源碼流程如下圖

getAdaptiveExtensionClass方法判斷是否存在類(lèi)級(jí)別自適應(yīng)擴(kuò)展點(diǎn),如果不存在則動(dòng)態(tài)創(chuàng)建
public?class?ExtensionLoader<T>?{
????private?Class>?getAdaptiveExtensionClass()?{
????????//?加載擴(kuò)展點(diǎn)并設(shè)置cachedAdaptiveClass
????????getExtensionClasses();
????????//?存在類(lèi)級(jí)別自適應(yīng)擴(kuò)展點(diǎn)則直接返回
????????if?(cachedAdaptiveClass?!=?null)?{
????????????return?cachedAdaptiveClass;
????????}
????????//?不存在類(lèi)級(jí)別自適應(yīng)擴(kuò)展點(diǎn)則動(dòng)態(tài)創(chuàng)建
????????return?cachedAdaptiveClass?=?createAdaptiveExtensionClass();
????}
}
createAdaptiveExtensionClass方法動(dòng)態(tài)生成自適應(yīng)擴(kuò)展點(diǎn)代碼
public?class?ExtensionLoader<T>?{
????private?Class>?createAdaptiveExtensionClass()?{
????????//?動(dòng)態(tài)生成自適應(yīng)擴(kuò)展點(diǎn)代碼
????????//?此方法會(huì)校驗(yàn)是否滿(mǎn)足以下任意一個(gè)條件
????????//?1.方法必須包含URL輸入?yún)?shù)
????????//?2.方法必須包含get方法且返回值為URL
????????String?code?=?createAdaptiveExtensionClassCode();
????????//?javassist編譯生成class對(duì)象
????????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);
????}
}
StockSpiService$Adaptive為動(dòng)態(tài)生成自適應(yīng)擴(kuò)展點(diǎn)代碼,我們可以看到URL這個(gè)參數(shù)作用,可以類(lèi)比URL為路由器,自適應(yīng)擴(kuò)展點(diǎn)根據(jù)輸入?yún)?shù)決定路由到哪個(gè)服務(wù),如果沒(méi)有值則默認(rèn)路由到stockA服務(wù)
package?com.java.front.dubbo.spi.api.stock;
import?org.apache.dubbo.common.extension.ExtensionLoader;
public?class?StockSpiService$Adaptive?implements?com.java.front.dubbo.spi.api.stock.StockSpiService?{
????public?boolean?reduceStock1(java.lang.String?arg0,?org.apache.dubbo.common.URL?arg1)?{
????????if?(arg1?==?null)
????????????throw?new?IllegalArgumentException("url?==?null");
????????org.apache.dubbo.common.URL?url?=?arg1;
????????String?extName?=?url.getParameter("bizType",?"stockA");
????????if?(extName?==?null)
????????????throw?new?IllegalStateException("Fail?to?get?extension(com.java.front.dubbo.spi.api.stock.StockSpiService)?name?from?url("?+?url.toString()?+?")?use?keys([bizType])");
????????com.java.front.dubbo.spi.api.stock.StockSpiService?extension?=?(com.java.front.dubbo.spi.api.stock.StockSpiService)?ExtensionLoader.getExtensionLoader(com.java.front.dubbo.spi.api.stock.StockSpiService.class).getExtension(extName);
????????return?extension.reduceStock1(arg0,?arg1);
????}
????public?boolean?reduceStock2(com.java.front.dubbo.spi.model.StockReduceModel?arg0)?{
????????if?(arg0?==?null)
????????????throw?new?IllegalArgumentException("com.java.front.dubbo.spi.model.StockReduceModel?argument?==?null");
????????if?(arg0.getUrl()?==?null)
????????????throw?new?IllegalArgumentException("com.java.front.dubbo.spi.model.StockReduceModel?argument?getUrl()?==?null");
????????org.apache.dubbo.common.URL?url?=?arg0.getUrl();
????????String?extName?=?url.getParameter("bizType",?"stockA");
????????if?(extName?==?null)
????????????throw?new?IllegalStateException("Fail?to?get?extension(com.java.front.dubbo.spi.api.stock.StockSpiService)?name?from?url("?+?url.toString()?+?")?use?keys([bizType])");
????????com.java.front.dubbo.spi.api.stock.StockSpiService?extension?=?(com.java.front.dubbo.spi.api.stock.StockSpiService)?ExtensionLoader.getExtensionLoader(com.java.front.dubbo.spi.api.stock.StockSpiService.class).getExtension(extName);
????????return?extension.reduceStock2(arg0);
????}
}
6 自實(shí)現(xiàn)IOC
6.1 代碼實(shí)例
第一步新增商品接口
package?com.java.front.dubbo.spi.api.goods;
import?org.apache.dubbo.common.URL;
import?org.apache.dubbo.common.extension.SPI;
@SPI
public?interface?GoodsSpiService?{
????public?boolean?buyGoods(String?skuId,?URL?url);
}
第二步實(shí)現(xiàn)商品接口
package?com.java.front.dubbo.spi.impl.goods;
import?org.apache.dubbo.common.URL;
import?com.java.front.dubbo.spi.api.goods.GoodsSpiService;
import?com.java.front.dubbo.spi.api.stock.StockSpiService;
public?class?GoodsSpiAServiceImpl?implements?GoodsSpiService?{
????private?StockSpiService?stockSpiService;
????public?void?setStockSpiService(StockSpiService?stockSpiService)?{
????????this.stockSpiService?=?stockSpiService;
????}
????@Override
????public?boolean?buyGoods(String?skuId,?URL?url)?{
????????System.out.println("GoodsSpiAService?buyGoods?skuId="?+?skuId);
????????stockSpiService.reduceStock1(skuId,?url);
????????return?false;
????}
}
第三步新增配置文件
├─src
│ ├─main
│ │ └─resources
│ │ └─META-INF
│ │ ├─services
│ │ │ com.java.front.dubbo.spi.api.stock.GoodsSpiService
第四步新增配置文件內(nèi)容
goodsA=com.java.front.dubbo.spi.impl.goods.GoodsSpiAServiceImpl
第五步運(yùn)行測(cè)試代碼
public?class?GoodsServiceTest?{
????public?static?void?main(String[]?args)?{
????????test1();
????}
????public?static?void?test1()?{
????????ExtensionLoader?extensionLoader?=?ExtensionLoader.getExtensionLoader(GoodsSpiService.class);
????????GoodsSpiService?goodsService?=?extensionLoader.getExtension("goodsA");
????????Map?map?=?new?HashMap<>();
????????map.put("bizType",?"stockA");
????????URL?url?=?new?URL(StringUtils.EMPTY,?StringUtils.EMPTY,?1,?map);
????????goodsService.buyGoods("skuId_111",?url);
????}
}
第六步輸出測(cè)試結(jié)果
GoodsSpiAService buyGoods skuId=skuId_111
StockSpiAService reduceStock1 skuId=skuId_111
6.2 源碼分析
源碼流程如下圖

injectExtension方法自實(shí)現(xiàn)IOC
public?class?ExtensionLoader<T>?{
????private?T?createExtension(String?name)?{
????????Class>?clazz?=?getExtensionClasses().get(name);
????????if?(clazz?==?null)?{
????????????throw?findException(name);
????????}
????????try?{
????????????//?instance?=?com.java.front.dubbo.spi.impl.goods.GoodsSpiAServiceImpl
????????????T?instance?=?(T)?EXTENSION_INSTANCES.get(clazz);
????????????if?(instance?==?null)?{
????????????????EXTENSION_INSTANCES.putIfAbsent(clazz,?clazz.newInstance());
????????????????instance?=?(T)?EXTENSION_INSTANCES.get(clazz);
????????????}
????????????//?自實(shí)現(xiàn)IOC
????????????injectExtension(instance);
????????????//?省略代碼
????????????return?instance;
????????}?catch?(Throwable?t)?{
????????????throw?new?IllegalStateException("Extension?instance(name:?"?+?name?+?",?class:?"?+?type?+?")??could?not?be?instantiated:?"?+?t.getMessage(),?t);
????????}
????}
????private?T?injectExtension(T?instance)?{
????????try?{
????????????if?(objectFactory?!=?null)?{
????????????????//?遍歷instance所有setxxx方法
????????????????for?(Method?method?:?instance.getClass().getMethods())?{
????????????????????if?(method.getName().startsWith("set")
????????????????????????????&&?method.getParameterTypes().length?==?1
????????????????????????????&&?Modifier.isPublic(method.getModifiers()))?{
????????????????????????if?(method.getAnnotation(DisableInject.class)?!=?null)?{
????????????????????????????continue;
????????????????????????}
????????????????????????//?method?=?public?void?com.java.front.dubbo.spi.impl.goods.GoodsSpiAServiceImpl.setStockSpiService(com.java.front.dubbo.spi.api.stock.StockSpiService)
????????????????????????//?pt?=?com.java.front.dubbo.spi.api.stock.StockSpiService
????????????????????????Class>?pt?=?method.getParameterTypes()[0];
????????????????????????if?(ReflectUtils.isPrimitives(pt))?{
????????????????????????????continue;
????????????????????????}
????????????????????????try?{
????????????????????????????//?property?=?stockSpiService
????????????????????????????String?property?=?method.getName().length()?>?3???method.getName().substring(3,?4).toLowerCase()?+?method.getName().substring(4)?:?StringUtils.EMPTY;
????????????????????????????//?objectFactory?=?AdaptiveExtensionFactory
????????????????????????????//?AdaptiveExtensionFactory.getExtension依次執(zhí)行SpiExtensionFactory、SpringExtensionFactory直到獲取到StockSpiService對(duì)象賦值給object
????????????????????????????Object?object?=?objectFactory.getExtension(pt,?property);
????????????????????????????if?(object?!=?null)?{
????????????????????????????
????????????????????????????????//?instance?=?com.java.front.dubbo.spi.impl.goods.GoodsSpiAServiceImpl
????????????????????????????????//?method?=?GoodsSpiAServiceImpl.setStockSpiService
????????????????????????????????//?object?=?StockSpiService$Adaptive
????????????????????????????????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;
????}
}
public?class?SpiExtensionFactory?implements?ExtensionFactory?{
????@Override
????public??T?getExtension(Class?type,?String?name) ?{
????????if?(type.isInterface()?&&?type.isAnnotationPresent(SPI.class))?{
????????????ExtensionLoader?loader?=?ExtensionLoader.getExtensionLoader(type);
????????????if?(!loader.getSupportedExtensions().isEmpty())?{
????????????????return?loader.getAdaptiveExtension();
????????????}
????????}
????????return?null;
????}
}
7 自實(shí)現(xiàn)AOP
7.1 裝飾器模式
裝飾器模式可以動(dòng)態(tài)將責(zé)任附加到對(duì)象上,在不改變?cè)碱?lèi)接口情況下對(duì)原始類(lèi)功能進(jìn)行增強(qiáng),并且支持多個(gè)裝飾器的嵌套使用,實(shí)現(xiàn)裝飾器模式需要以下組件:
(1) Component
抽象構(gòu)件:核心業(yè)務(wù)抽象,使用接口或者抽象類(lèi)
public?abstract?class?Component?{
????public?abstract?void?playFootBall();
}
(2) ConcreteComponent
具體構(gòu)件:核心業(yè)務(wù)代碼
public?class?ConcreteComponent?extends?Component?{
????@Override
????public?void?playFootBall()?{
????????System.out.println("踢足球");
????}
}
(3) Decorator
抽象裝飾器:繼承Component抽象類(lèi)并通過(guò)構(gòu)造函數(shù)組合Component
public?abstract?class?Decorator?extends?Component?{
????private?Component?component?=?null;
????public?Decorator(Component?component)?{
????????this.component?=?component;
????}
????@Override
????public?void?playFootBall()?{
????????this.component.playFootBall();
????}
}
(4) ConcreteDecorator
具體裝飾器:具體裝飾代碼
//?球襪裝飾器
public?class?ConcreteDecoratorA?extends?Decorator?{
????public?ConcreteDecoratorA(Component?component)?{
????????super(component);
????}
????private?void?decorateMethod()?{
????????System.out.println("換球襪");
????}
????@Override
????public?void?playFootBall()?{
????????this.decorateMethod();
????????super.playFootBall();
????}
}
//?球鞋裝飾器
public?class?ConcreteDecoratorB?extends?Decorator?{
????public?ConcreteDecoratorB(Component?component)?{
????????super(component);
????}
????private?void?decorateMethod()?{
????????System.out.println("換球鞋");
????}
????@Override
????public?void?playFootBall()?{
????????this.decorateMethod();
????????super.playFootBall();
????}
}
(5) 測(cè)試代碼
public?class?TestDecoratorDemo?{
????public?static?void?main(String[]?args)?{
????????Component?component?=?new?ConcreteComponent();
????????component?=?new?ConcreteDecoratorA(component);
????????component?=?new?ConcreteDecoratorB(component);
????????component.playFootBall();
????}
}
(6) 輸出結(jié)果
換球鞋
換球襪
踢足球
7.2 代碼實(shí)例
本章節(jié)在第二章節(jié)訂單服務(wù)基礎(chǔ)上進(jìn)行擴(kuò)展,第一步新增兩個(gè)切面,一個(gè)日志切面,一個(gè)事務(wù)切面。我們可以理解切面為裝飾器:切面實(shí)現(xiàn)了訂單服務(wù),并通過(guò)構(gòu)造函數(shù)組合了訂單服務(wù)
package?com.java.front.dubbo.spi.impl.order;
//?日志切面
public?class?OrderSpiLogWrapper?implements?OrderSpiService?{
????private?OrderSpiService?orderSpiService;
????public?OrderSpiLogWrapper(OrderSpiService?orderSpiService)?{
????????this.orderSpiService?=?orderSpiService;
????}
????@Override
????public?boolean?createOrder(OrderModel?order)?{
????????System.out.println("OrderSpiLogWrapper?log?start");
????????boolean?result?=?orderSpiService.createOrder(order);
????????System.out.println("OrderSpiLogWrapper?log?end");
????????return?result;
????}
}
//?事務(wù)切面
public?class?OrderSpiTransactionWrapper?implements?OrderSpiService?{
????private?OrderSpiService?orderSpiService;
????public?OrderSpiTransactionWrapper(OrderSpiService?orderSpiService)?{
????????this.orderSpiService?=?orderSpiService;
????}
????@Override
????public?boolean?createOrder(OrderModel?order)?{
????????System.out.println("OrderSpiTransactionWrapper?begin");
????????boolean?result?=?orderSpiService.createOrder(order);
????????System.out.println("OrderSpiTransactionWrapper?commit");
????????return?result;
????}
}
第二步新增配置文件內(nèi)容
orderSpiLogWrapper=com.java.front.dubbo.spi.impl.order.OrderSpiLogWrapper
orderSpiTransactionWrapper=com.java.front.dubbo.spi.impl.order.OrderSpiTransactionWrapper
第三步運(yùn)行測(cè)試代碼
public?class?OrderSpiServiceTest?{
????public?static?void?main(String[]?args)?{
????????test1();
????}
????public?static?void?test1()?{
????????ExtensionLoader?extensionLoader?=?ExtensionLoader.getExtensionLoader(OrderSpiService.class);
????????OrderSpiService?orderSpiService?=?extensionLoader.getExtension("orderSpiAService");
????????orderSpiService.createOrder(new?OrderModel());
????}
}
第四步輸出測(cè)試結(jié)果
OrderSpiLogWrapper log start
OrderSpiTransactionWrapper begin
OrderSpiAService createOrder
OrderSpiTransactionWrapper commit
OrderSpiLogWrapper log end
7.3 源碼分析
源碼流程如下圖

createExtension方法可以看到切面通過(guò)構(gòu)造函數(shù)裝飾訂單服務(wù)
public?class?ExtensionLoader<T>?{
????private?T?createExtension(String?name)?{
????????Class>?clazz?=?getExtensionClasses().get(name);
????????if?(clazz?==?null)?{
????????????throw?findException(name);
????????}
????????try?{
????????????//?instance?=?com.java.front.dubbo.spi.impl.order.OrderSpiAServiceImpl
????????????T?instance?=?(T)?EXTENSION_INSTANCES.get(clazz);
????????????if?(instance?==?null)?{
????????????????EXTENSION_INSTANCES.putIfAbsent(clazz,?clazz.newInstance());
????????????????instance?=?(T)?EXTENSION_INSTANCES.get(clazz);
????????????}
????????????//?自實(shí)現(xiàn)IOC
????????????injectExtension(instance);
????????????//?所有切面
????????????//?wrapperClasses?=?[com.java.front.dubbo.spi.impl.order.OrderSpiLogWrapper,com.java.front.dubbo.spi.impl.order.OrderSpiTransactionWrapper]
????????????Set>?wrapperClasses?=?cachedWrapperClasses;
????????????//?自實(shí)現(xiàn)AOP
????????????//?instance=(OrderSpiLogWrapper(OrderSpiTransactionWrapper(orderSpiAService)))
????????????if?(CollectionUtils.isNotEmpty(wrapperClasses))?{
????????????????for?(Class>?wrapperClass?:?wrapperClasses)?{
????????????????????//?1.通過(guò)切面構(gòu)造函數(shù)進(jìn)行裝飾
????????????????????//?2.切面可能需要通過(guò)set注入屬性所以執(zhí)行injectExtension
????????????????????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);
????????}
????}
}
loadClass方法設(shè)置cachedWrapperClasses
public?class?ExtensionLoader<T>?{
????private?void?loadClass(Map>?extensionClasses,?java.net.URL?resourceURL,?Class>?clazz,?String?name) ?throws?NoSuchMethodException?{
????????//?clazz是否實(shí)現(xiàn)type
????????//?type?=?com.java.front.dubbo.spi.api.order.OrderSpiService
????????//?clazz?=?com.java.front.dubbo.spi.impl.order.OrderSpiLogWrapper
????????//?clazz?=?com.java.front.dubbo.spi.impl.order.OrderSpiTransactionWrapper
????????if?(!type.isAssignableFrom(clazz))?{
????????????throw?new?IllegalStateException("Error?when?load?extension?class(interface:?"?+?type?+?",?class?line:?"?+?clazz.getName()?+?"),?class?"?+?clazz.getName()?+?"is?not?subtype?of?interface.");
????????}
????????//?省略代碼
????????//?clazz是不是wrapper
????????else?if?(isWrapperClass(clazz))?{
????????????Set>?wrappers?=?cachedWrapperClasses;
????????????if?(wrappers?==?null)?{
????????????????cachedWrapperClasses?=?new?ConcurrentHashSet>();
????????????????wrappers?=?cachedWrapperClasses;
????????????}
????????????//?新增至集合cachedWrapperClasses
????????????wrappers.add(clazz);
????????}
????????//?省略代碼
????}
????//?clazz是否通過(guò)構(gòu)造函數(shù)組合type
????private?boolean?isWrapperClass(Class>?clazz)?{
????????try?{
????????????clazz.getConstructor(type);
????????????return?true;
????????}?catch?(NoSuchMethodException?e)?{
????????????return?false;
????????}
????}
}
8 文章總結(jié)
本文通過(guò)代碼實(shí)例與源碼分析兩種方式,詳細(xì)分析了DUBBO SPI六個(gè)特性:指定具體擴(kuò)展點(diǎn)、指定默認(rèn)擴(kuò)展點(diǎn)、類(lèi)級(jí)別自適應(yīng)擴(kuò)展點(diǎn)、方法級(jí)自適應(yīng)擴(kuò)展點(diǎn)、自實(shí)現(xiàn)IOC、自實(shí)現(xiàn)AOP,希望本文對(duì)大家有所幫助。
JAVA前線?
歡迎大家關(guān)注公眾號(hào)「JAVA前線」查看更多精彩分享,主要內(nèi)容包括源碼分析、實(shí)際應(yīng)用、架構(gòu)思維、職場(chǎng)分享、產(chǎn)品思考等等,同時(shí)也非常歡迎大家加我微信「java_front」一起交流學(xué)習(xí)
