1. 一文講清楚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)類全限定名配置在文件,并由服務(wù)加載器讀取配置文件加載實(shí)現(xiàn)類,這樣可以在運(yùn)行時(shí)動(dòng)態(tài)為接口替換實(shí)現(xiàn)類,通過SPI機(jī)制可以為程序提供拓展功能。

        我們之前在文章《JDK SPI機(jī)制》已經(jīng)討論了JDK SPI如何實(shí)現(xiàn),在文章《SLF4J源碼角度分析阿里開發(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)
        • 類級(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)類
        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表示自定義名稱、Value表示具體實(shí)現(xiàn)類
        ????????//?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)類
        ????????????????????????????????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ù)輸入名稱獲取擴(kuò)展點(diǎn)

        public?class?ExtensionLoader<T>?{

        ????private?T?createExtension(String?name)?{
        ????????//?根據(jù)name獲取對(duì)應(yīng)實(shí)現(xiàn)類
        ????????Class?clazz?=?getExtensionClasses().get(name);
        ????????if?(clazz?==?null)?{
        ????????????throw?findException(name);
        ????????}
        ????????try?{
        ????????????//?獲取實(shí)現(xiàn)類對(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)類并設(shè)置cachedDefaultName
        ????????getExtensionClasses();
        ????????if?(null?==?cachedDefaultName?||?cachedDefaultName.length()?==?0?||?"true".equals(cachedDefaultName))?{
        ????????????return?null;
        ????????}
        ????????//?根據(jù)默認(rèn)名稱獲取擴(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表示自定義名稱、Value表示具體實(shí)現(xiàn)類
        ????????//?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 類級(jí)別自適應(yīng)擴(kuò)展點(diǎn)

        4.1 代碼實(shí)例

        第一步新增訂單自適應(yīng)實(shí)現(xiàn)類

        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方法判斷是否存在類級(jí)別自適應(yīng)擴(kuò)展點(diǎn),如果存在則直接返回

        public?class?ExtensionLoader<T>?{

        ????private?Class?getAdaptiveExtensionClass()?{

        ????????//?加載擴(kuò)展點(diǎn)并設(shè)置cachedAdaptiveClass
        ????????getExtensionClasses();

        ????????//?存在類級(jí)別自適應(yīng)擴(kuò)展點(diǎn)則直接返回
        ????????if?(cachedAdaptiveClass?!=?null)?{
        ????????????return?cachedAdaptiveClass;
        ????????}

        ????????//?不存在類級(jí)別自適應(yīng)擴(kuò)展點(diǎn)則動(dòng)態(tài)創(chuàng)建
        ????????return?cachedAdaptiveClass?=?createAdaptiveExtensionClass();
        ????}
        }

        loadClass方法發(fā)現(xiàn)存在類級(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)類
        ????????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.");
        ????????}

        ????????//?存在類級(jí)別自適應(yīng)擴(kuò)展點(diǎn)則設(shè)置cachedAdaptiveClass
        ????????if?(clazz.isAnnotationPresent(Adaptive.class))?{
        ????????????if?(cachedAdaptiveClass?==?null)?{
        ????????????????cachedAdaptiveClass?=?clazz;
        ????????????}
        ????????????//?一個(gè)接口只允許有一個(gè)類級(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í)例

        第一步新建庫存接口與庫存實(shí)體,使用方法級(jí)別自適應(yīng)擴(kuò)展點(diǎ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)庫存服務(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方法判斷是否存在類級(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();

        ????????//?存在類級(jí)別自適應(yīng)擴(kuò)展點(diǎn)則直接返回
        ????????if?(cachedAdaptiveClass?!=?null)?{
        ????????????return?cachedAdaptiveClass;
        ????????}

        ????????//?不存在類級(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)是否滿足以下任意一個(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ù)作用,可以類比URL為路由器,自適應(yīng)擴(kuò)展點(diǎn)根據(jù)輸入?yún)?shù)決定路由到哪個(gè)服務(wù),如果沒有值則默認(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è)碱惤涌谇闆r下對(duì)原始類功能進(jìn)行增強(qiáng),并且支持多個(gè)裝飾器的嵌套使用,實(shí)現(xiàn)裝飾器模式需要以下組件:


        (1) Component

        抽象構(gòu)件:核心業(yè)務(wù)抽象,使用接口或者抽象類

        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抽象類并通過構(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ù),并通過構(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方法可以看到切面通過構(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.通過切面構(gòu)造函數(shù)進(jìn)行裝飾
        ????????????????????//?2.切面可能需要通過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是否通過構(gòu)造函數(shù)組合type
        ????private?boolean?isWrapperClass(Class?clazz)?{
        ????????try?{
        ????????????clazz.getConstructor(type);
        ????????????return?true;
        ????????}?catch?(NoSuchMethodException?e)?{
        ????????????return?false;
        ????????}
        ????}
        }

        8 文章總結(jié)

        本文通過代碼實(shí)例與源碼分析兩種方式,詳細(xì)分析了DUBBO SPI六個(gè)特性:指定具體擴(kuò)展點(diǎn)、指定默認(rèn)擴(kuò)展點(diǎn)、類級(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)
          
          

            1. 88精品 | 国产精品成人免费看片 | 69精品无码成人久久久久久 | 做爱视频成人的免费观看 | 久久香焦网 |