<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一文講清楚DUBBO SPI機(jī)制六個(gè)特性

          共 21830字,需瀏覽 44分鐘

           ·

          2022-01-21 05:05


          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 源碼分析

          源碼流程如下圖


          loadExtensionClasses方法讀取相關(guān)目錄下配置文件并加載實(shí)現(xiàn)類(lèi)
          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í)

          瀏覽 83
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  AV黄色在线观看 | 欧美激情三级片 | 青娱乐最新视频 | 日本三级网站 | 欧美一级操逼视频 |