一文讲清楚DUBBO SPI机制六个特性

JAVA前线

共 21830字,需浏览 44分钟

 ·

2022-01-21 05:05


JAVA前线 


欢迎大家关注公众号「JAVA前线」查看更多精彩分享,主要内容包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时也非常欢迎大家加我微信「java_front」一起交流学习


1 文章概述

SPI(Service Provider Interface)是一种服务发现机制,本质是将接口实现类全限定名配置在文件,并由服务加载器读取配置文件加载实现类,这样可以在运行时动态为接口替换实现类,通过SPI机制可以为程序提供拓展功能。

我们之前在文章《JDK SPI机制》已经讨论了JDK SPI如何实现,在文章《SLF4J源码角度分析阿里开发手册日志规约》已经讨论了JDK SPI如何应用,本文我们分析DUBBO SPI机制,相较于JDK SPI至少进行了以下功能扩展:

  • 指定具体扩展点
  • 指定默认扩展点
  • 类级别自适应扩展点
  • 方法级自适应扩展点
  • 自实现IOC
  • 自实现AOP

2 指定具体扩展点

2.1 代码实例

第一步定义订单接口与订单模型

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;
}

第二步实现订单服务

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

第四步新增配置文件内容

orderSpiAService=com.java.front.dubbo.spi.impl.order.OrderSpiAServiceImpl
orderSpiBService=com.java.front.dubbo.spi.impl.order.OrderSpiBServiceImpl

第五步运行测试代码

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());
    }
}

第六步输出测试结果

OrderSpiAService createOrder

2.2 源码分析

源码流程如下图


loadExtensionClasses方法读取相关目录下配置文件并加载实现类
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表示具体实现类
        // 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;
                            // 等号作为分隔符
                            int i = line.indexOf('=');
                            if (i > 0) {
                                name = line.substring(0, i).trim();
                                line = line.substring(i + 1).trim();
                            }
                            if (line.length() > 0) {
                                // 加载实现类
                                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)根据输入名称获取扩展点

public class ExtensionLoader<T{

    private T createExtension(String name) {
        // 根据name获取对应实现类
        Class clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
            // 获取实现类对象
            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 指定默认扩展点

3.1 代码实例

第一步修改订单接口

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);
}

第二步运行测试代码

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());
    }
}

第三步输出测试结果

OrderSpiBService createOrder

3.2 源码分析

源码流程如下图


getDefaultExtension方法获取默认扩展点

public class ExtensionLoader<T{

    public T getDefaultExtension() {
        // 加载实现类并设置cachedDefaultName
        getExtensionClasses();
        if (null == cachedDefaultName || cachedDefaultName.length() == 0 || "true".equals(cachedDefaultName)) {
            return null;
        }
        // 根据默认名称获取扩展点
        return getExtension(cachedDefaultName);
    }
}

loadExtensionClasses方法设置默认扩展点名

public class ExtensionLoader<T{

    private Map> loadExtensionClasses() {

        // 一个接口只允许有一个默认扩展点
        // @SPI("orderSpiBService")表示设置orderSpiBService作为默认扩展点
        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表示具体实现类
        // 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 类级别自适应扩展点

4.1 代码实例

第一步新增订单自适应实现类

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

第三步运行测试代码

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());
    }
}

第四步输出测试结果

OrderSpiAdaptiveService createOrder

4.2 源码分析

源码流程如下图


getAdaptiveExtensionClass方法判断是否存在类级别自适应扩展点,如果存在则直接返回

public class ExtensionLoader<T{

    private Class getAdaptiveExtensionClass() {

        // 加载扩展点并设置cachedAdaptiveClass
        getExtensionClasses();

        // 存在类级别自适应扩展点则直接返回
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }

        // 不存在类级别自适应扩展点则动态创建
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }
}

loadClass方法发现存在类级别自适应扩展点则设置cachedAdaptiveClass

public class ExtensionLoader<T{

    private void loadClass(Map> extensionClasses, java.net.URL resourceURL, Class clazz, String name) throws NoSuchMethodException {

        // clazz是否为接口实现类
        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.");
        }

        // 存在类级别自适应扩展点则设置cachedAdaptiveClass
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            if (cachedAdaptiveClass == null) {
                cachedAdaptiveClass = clazz;
            }
            // 一个接口只允许有一个类级别自适应扩展点
            else if (!cachedAdaptiveClass.equals(clazz)) {
                throw new IllegalStateException("More than 1 adaptive class found: " + cachedAdaptiveClass.getClass().getName() + ", " + clazz.getClass().getName());
            }
        }

        // 省略代码
    }
}

5 方法级自适应扩展点

5.1 代码实例

第一步新建库存接口与库存实体,使用方法级别自适应扩展点需要满足以下任意一个条件:

  • 方法必须包含URL输入参数
  • 方法必须包含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;
    }
}

第二步实现库存服务

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

第四步新增配置文件内容

stockA=com.java.front.dubbo.spi.impl.stock.StockSpiAServiceImpl
stockB=com.java.front.dubbo.spi.impl.stock.StockSpiBServiceImpl

第五步运行测试代码

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);
    }
}

第六步输出测试结果

StockSpiBService reduceStock1 skuId=skuId_111
StockSpiBService reduceStock2 stockReduceModel=StockReduceModel(skuId=skuId_111, url=?bizType=stockB)

5.2 源码分析

源码流程如下图


getAdaptiveExtensionClass方法判断是否存在类级别自适应扩展点,如果不存在则动态创建

public class ExtensionLoader<T{

    private Class getAdaptiveExtensionClass() {

        // 加载扩展点并设置cachedAdaptiveClass
        getExtensionClasses();

        // 存在类级别自适应扩展点则直接返回
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }

        // 不存在类级别自适应扩展点则动态创建
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }
}

createAdaptiveExtensionClass方法动态生成自适应扩展点代码

public class ExtensionLoader<T{
    private Class createAdaptiveExtensionClass() {

        // 动态生成自适应扩展点代码
        // 此方法会校验是否满足以下任意一个条件
        // 1.方法必须包含URL输入参数
        // 2.方法必须包含get方法且返回值为URL
        String code = createAdaptiveExtensionClassCode();

        // javassist编译生成class对象
        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为动态生成自适应扩展点代码,我们可以看到URL这个参数作用,可以类比URL为路由器,自适应扩展点根据输入参数决定路由到哪个服务,如果没有值则默认路由到stockA服务

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 自实现IOC

6.1 代码实例

第一步新增商品接口

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);
}

第二步实现商品接口

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

第四步新增配置文件内容

goodsA=com.java.front.dubbo.spi.impl.goods.GoodsSpiAServiceImpl

第五步运行测试代码

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);
    }
}

第六步输出测试结果

GoodsSpiAService buyGoods skuId=skuId_111
StockSpiAService reduceStock1 skuId=skuId_111

6.2 源码分析

源码流程如下图


injectExtension方法自实现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);
            }

            // 自实现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(34).toLowerCase() + method.getName().substring(4) : StringUtils.EMPTY;

                            // objectFactory = AdaptiveExtensionFactory
                            // AdaptiveExtensionFactory.getExtension依次执行SpiExtensionFactory、SpringExtensionFactory直到获取到StockSpiService对象赋值给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  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 自实现AOP

7.1 装饰器模式

装饰器模式可以动态将责任附加到对象上,在不改变原始类接口情况下对原始类功能进行增强,并且支持多个装饰器的嵌套使用,实现装饰器模式需要以下组件:


(1) Component

抽象构件:核心业务抽象,使用接口或者抽象类

public abstract class Component {
    public abstract void playFootBall();
}

(2) ConcreteComponent

具体构件:核心业务代码

public class ConcreteComponent extends Component {

    @Override
    public void playFootBall() {
        System.out.println("踢足球");
    }
}

(3) Decorator

抽象装饰器:继承Component抽象类并通过构造函数组合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) 测试代码

public class TestDecoratorDemo {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
        component = new ConcreteDecoratorA(component);
        component = new ConcreteDecoratorB(component);
        component.playFootBall();
    }
}

(6) 输出结果

换球鞋
换球袜
踢足球

7.2 代码实例

本章节在第二章节订单服务基础上进行扩展,第一步新增两个切面,一个日志切面,一个事务切面。我们可以理解切面为装饰器:切面实现了订单服务,并通过构造函数组合了订单服务

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;
    }
}


// 事务切面
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;
    }
}

第二步新增配置文件内容

orderSpiLogWrapper=com.java.front.dubbo.spi.impl.order.OrderSpiLogWrapper
orderSpiTransactionWrapper=com.java.front.dubbo.spi.impl.order.OrderSpiTransactionWrapper

第三步运行测试代码

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());
    }
}

第四步输出测试结果

OrderSpiLogWrapper log start
OrderSpiTransactionWrapper begin
OrderSpiAService createOrder
OrderSpiTransactionWrapper commit
OrderSpiLogWrapper log end

7.3 源码分析

源码流程如下图


createExtension方法可以看到切面通过构造函数装饰订单服务

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);
            }

            // 自实现IOC
            injectExtension(instance);

            // 所有切面
            // wrapperClasses = [com.java.front.dubbo.spi.impl.order.OrderSpiLogWrapper,com.java.front.dubbo.spi.impl.order.OrderSpiTransactionWrapper]
            Set> wrapperClasses = cachedWrapperClasses;

            // 自实现AOP
            // instance=(OrderSpiLogWrapper(OrderSpiTransactionWrapper(orderSpiAService)))
            if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                for (Class wrapperClass : wrapperClasses) {
                    // 1.通过切面构造函数进行装饰
                    // 2.切面可能需要通过set注入属性所以执行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方法设置cachedWrapperClasses

public class ExtensionLoader<T{
    private void loadClass(Map> extensionClasses, java.net.URL resourceURL, Class clazz, String name) throws NoSuchMethodException {

        // clazz是否实现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是否通过构造函数组合type
    private boolean isWrapperClass(Class clazz) {
        try {
            clazz.getConstructor(type);
            return true;
        } catch (NoSuchMethodException e) {
            return false;
        }
    }
}

8 文章总结

本文通过代码实例与源码分析两种方式,详细分析了DUBBO SPI六个特性:指定具体扩展点、指定默认扩展点、类级别自适应扩展点、方法级自适应扩展点、自实现IOC、自实现AOP,希望本文对大家有所帮助。




JAVA前线 


欢迎大家关注公众号「JAVA前线」查看更多精彩分享,主要内容包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时也非常欢迎大家加我微信「java_front」一起交流学习

浏览 41
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报