Dubbo SPI实现原理

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

1 SPI用法

1.1 普通扩展

1.1.1 创建带有SPI注解的接口与实现类

@SPI
public interface DemoSpi {
    //此处的url在加载自适应扩展中可以用到
    void say(URL url);
}
public class DemoSpiImpl implements DemoSpi{

    Property property;
    @Override
    public void say(URL url) {
        System.out.println("Hello DemoSpiImpl");
    }

    public Property getProperty() {
        return property;
    }

    public void setProperty(Property property) {
        this.property = property;
    }
}

1.1.2 创建配置文件

在 META-INF 文件夹下创建dubbo文件夹,在dubbo文件夹下创建 com.shopping.demo24.dubbo.spi.DemoSpi 文件,内容如下:

demoSpiImpl=com.shopping.demo24.dubbo.spi.DemoSpiImpl

1.1.3 获取扩展

ExtensionLoader<DemoSpi> extensionLoader = ExtensionLoader.getExtensionLoader(DemoSpi.class);
URL url = URL.valueOf("demo://localhost/demo");
//通过扩展名获取扩展
DemoSpi demoSpi = extensionLoader.getExtension("demoSpiImpl");
demoSpi.say(url);    //这里会输出 Hello DemoSpiImpl

1.2 自适应扩展

如果有多个扩展类时,自适应扩展可以根据配置或URL的值来加载特定的扩展。

扩展类默认是通过Javassist自动生成源码,然后加载的。在生成的代码中通过扩展名来获取具体的实现类,并调用其方法。所以此时可以通过 url 来控制具体调用的扩展时哪个。

生成的代码如下:

public class DemoSpi$Adaptive implements com.shopping.demo24.dubbo.spi.DemoSpi {
    public void say(org.apache.dubbo.common.URL arg0) {
        if (arg0 == null) throw new IllegalArgumentException("url == null");
        org.apache.dubbo.common.URL url = arg0;
        String extName = url.getParameter("demo.spi", "d2");
        if (extName == null)
            throw new IllegalStateException("Failed to get extension (com.shopping.demo24.dubbo.spi.DemoSpi) name from url (" + url.toString() + ") use keys([demo.spi])");
        ScopeModel scopeModel = ScopeModelUtil.getOrDefault(url.getScopeModel(), com.shopping.demo24.dubbo.spi.DemoSpi.class);
        com.shopping.demo24.dubbo.spi.DemoSpi extension = (com.shopping.demo24.dubbo.spi.DemoSpi) scopeModel.getExtensionLoader(com.shopping.demo24.dubbo.spi.DemoSpi.class).getExtension(extName);
        extension.say(arg0);
    }
}

1.2.1 示例代码

在上一节的基础上再创建两个扩展类,给接口的SPI注解添加value,值为d2,给DemoSpiImpl1添加了注解@Adaptive。

@SPI("d2")
public interface DemoSpi {

    @Adaptive({"key"})
    void say(URL url);
}

@Adaptive
public class DemoSpiImpl1 implements DemoSpi{

    @Override
    public void say(URL url) {
        System.out.println("Hello DemoSpiImpl1");
    }

}

public class DemoSpiImpl2 implements DemoSpi{
    @Override
    public void say(URL url) {
        System.out.println("Hello DemoSpiImpl2");
    }
}

修改配置文件

d0=com.shopping.demo24.dubbo.spi.DemoSpiImpl
d1=com.shopping.demo24.dubbo.spi.DemoSpiImpl1
d2=com.shopping.demo24.dubbo.spi.DemoSpiImpl2

1.2.2 获取扩展的逻辑

1. 带有@Adaptive注解实现类的优先级最高。如果有实现带有@Adaptive注解,则获取的扩展不是动态生成的类,而是原始的实现类。

2. 如果URL参数中配置扩展名的话,最终方法调用会被代理到配置的扩展类实现上。如果接口方法上使用注解指定了key,则url中的参数需要配置为指定的key,如果没有指定,那默认的参数为 用"."分割的接口的小写名称。例如:上述示例注解为@Adaptive({"key"}),则url中的参数为 key=d0,这样调用方法时,获取名称为 d0 的扩展类。如果注解中没有指定,那url种可以配置为demo.spi=d0,这样获取的也是d0。

3. 如果url中没有指定,则默认获取的是 SPI 注解中指定的扩展。

4. 如果通过上面的步骤无法找到唯一的自适应扩展,就会抛出异常

java.lang.IllegalStateException: Failed to create adaptive instance: java.lang.IllegalStateException: Can't create adaptive extension interface com.shopping.demo24.dubbo.spi.DemoSpi, cause: No adaptive method exist on extension com.shopping.demo24.dubbo.spi.DemoSpi, refuse to create the adaptive class!

获取自适应扩展

//        配置自适应扩展
//        1.设置SPI注解的值为d2,@SPI("d2")
        demoSpi=extensionLoader.getAdaptiveExtension();
        demoSpi.say(url);   

        //2. url中的参数k与Adaptive中的参数相同,默认参数为 DemoSpi->demo.spi
        url = URL.valueOf("demo://localhost/demo?demo.spi=d0");
        url = URL.valueOf("demo://localhost/demo?key=d0");
        demoSpi=extensionLoader.getAdaptiveExtension();
        demoSpi.say(url);

        //3.在类DemoSpiImpl1上添加@Adaptive,设置 SPI注解的值为d2,获取的是d1,此时获取的是实现类,而不是生成的类
//        所以 @Adaptive注解的优先级最高
        demoSpi=extensionLoader.getAdaptiveExtension();
        demoSpi.say(url);

1.3 Activate注解的使用

@Activita注解,顾名思义,就是当存在多个扩展时,激活某一个扩展。其定义如下: 

public @interface Activate {
    String[] group() default {};
    String[] value() default {};
    int order() default 0;
}

group与value可以在获取激活的扩展时用来过滤,order是对获取的扩展进行排序,值越小,在list中排的就越靠前。

1.3.1 实例代码

@SPI
public interface DemoExt {
    void say();
}

@Activate(group = {"g1","g2"},value = {"k1:v1","k2,v2"},order = 2)
public class DemoExtImplA implements DemoExt{
    @Override
    public void say() {
        System.out.println("DemoExtImplA");
    }
}

@Activate(group = {"g1"},value = "k1:v1",order = 3)
public class DemoExtImplB implements DemoExt{
    @Override
    public void say() {
        System.out.println("DemoExtImplA");
    }
}

@Activate(group = {"g1","g3"},order = 1)
public class DemoExtImplC implements DemoExt{
    @Override
    public void say() {
        System.out.println("DemoExtImplA");
    }
}

1.3.2 配置文件

exta=com.shopping.demo24.dubbo.spi.activate.DemoExtImplA
extb=com.shopping.demo24.dubbo.spi.activate.DemoExtImplB
extc=com.shopping.demo24.dubbo.spi.activate.DemoExtImplC

1.3.3 获取激活的扩展

   @Test
    public void spiActivate() throws Exception {
        ExtensionLoader<DemoExt> loader = ExtensionLoader.getExtensionLoader(DemoExt.class);
        URL url = URL.valueOf("test://localhost/test");

        // 1. 组参数为空,不会用组过滤,只会用value过滤
        List<DemoExt> list = null;
               list = loader.getActivateExtension(url, new String[]{}, null);
        System.out.println(list);
//        [com.shopping.demo24.dubbo.spi.activate.DemoExtImplC@301770d9,
//        com.shopping.demo24.dubbo.spi.activate.DemoExtImplB@40edc64e]

        //2. 组匹配
        list = loader.getActivateExtension(url, new String[]{}, "g1");
        System.out.println(list);
//        [com.shopping.demo24.dubbo.spi.activate.DemoExtImplC@301770d9]

        //3. 组与value,注解中如果设置了value,则要求value中的key:value必须与url中的相同,
        // 注解中如果没有value,则不会校验参数,只用group匹配
        url=url.addParameter("k1","v1");
        list = loader.getActivateExtension(url, new String[]{}, "g1");
        System.out.println(list);
//        [com.shopping.demo24.dubbo.spi.activate.DemoExtImplC@e1fd2bf,
//        com.shopping.demo24.dubbo.spi.activate.DemoExtImplA@301770d9,
//        com.shopping.demo24.dubbo.spi.activate.DemoExtImplB@40edc64e]
    }

1.3.4 激活的扩展匹配逻辑

  1. 如果注解中有value,则获取激活的扩展时,会通过value中的值与url中的参数做匹配。
  2. 如果没有value,当获取激活的扩展时传入group,就会通过group过滤,如果没有传入group,就不糊通过group过滤。
  3. 如果获取了多个扩展,则order值越小的在list中的位置越靠前。 

2. 源码分析

2.1 获取ExtensionLoader

ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(DemoSpi.class);

ExtensionDirector.java
@Override
@SuppressWarnings("unchecked")
public <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    checkDestroyed();
    if (type == null) {
        throw new IllegalArgumentException("Extension type == null");
    }
    if (!type.isInterface()) {
        throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
    }
    if (!withExtensionAnnotation(type)) {
        throw new IllegalArgumentException("Extension type (" + type +
            ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
    }
    // 1. find in local cache
    // 从本地缓存获取
    ExtensionLoader<T> loader = (ExtensionLoader<T>) extensionLoadersMap.get(type);
    //获取扩展的范围,本例是 FRAMEWORK
    ExtensionScope scope = extensionScopeMap.get(type);
    if (scope == null) {
        SPI annotation = type.getAnnotation(SPI.class);
        scope = annotation.scope();
        extensionScopeMap.put(type, scope);
    }
    //如果扩展范围是 SELF 自己,那就创建扩展加载器
    if (loader == null && scope == ExtensionScope.SELF) {
        // create an instance in self scope
        loader = createExtensionLoader0(type);
    }
    //如果扩展范围不是自己,那就从父 ExtensionDirector(scoped extension loader manager) 获取加载器
    // 2. find in parent
    if (loader == null) {
        if (this.parent != null) {
            loader = this.parent.getExtensionLoader(type);
        }
    }
    // 3. create it
    // 没获取到的话创建
    if (loader == null) {
        loader = createExtensionLoader(type);
    }
    return loader;
}

private <T> ExtensionLoader<T> createExtensionLoader0(Class<T> type) {
    checkDestroyed();
    ExtensionLoader<T> loader;
    //这里会new一个ExtensionLoader,然后添加到extensionLoadersMap中
    extensionLoadersMap.putIfAbsent(type, new ExtensionLoader<T>(type, this, scopeModel));
    loader = (ExtensionLoader<T>) extensionLoadersMap.get(type);
    return loader;
}

2.2 通过名称获取扩展

DemoSpi demoSpi = extensionLoader.getExtension("d0");

 获取扩展时,先从Holder中获取,没有的话,创建扩展实例,再放入holder中。

 获取扩展实例前,首先要加载扩展类。这里使用的加载策略是:DubboLoadingStrategy

DubboLoadingStrategy会在META-INF/dubbo/中找配置文件,最终会找到 META-INF/dubbo/com.shopping.demo24.dubbo.spi.DemoSpi 文件,然后加载

 从配置类中解析出扩展的实现类,然后加载

 获取所有扩展类后,根据名称获取需要的扩展类,然后实例化,返回扩展类的实例。

2.3 获取自适应扩展

demoSpi=extensionLoader.getAdaptiveExtension();

ExtensionLoader

T getAdaptiveExtension()

public T getAdaptiveExtension() {
    //检查是否已经关闭
    checkDestroyed();
    //从缓存中获取实例
    Object instance = cachedAdaptiveInstance.get();
    if (instance == null) {
        // 如果存在错误,则抛出异常
        if (createAdaptiveInstanceError != null) {
            throw new IllegalStateException("Failed to create adaptive instance: " +
                createAdaptiveInstanceError.toString(),
                createAdaptiveInstanceError);
        }

        synchronized (cachedAdaptiveInstance) {
            instance = cachedAdaptiveInstance.get();
            //双重检查
            if (instance == null) {
                try {
                    //创建自适应扩展实例
                    instance = createAdaptiveExtension();
                    //将该实例缓存
                    cachedAdaptiveInstance.set(instance);
                } catch (Throwable t) {
                    createAdaptiveInstanceError = t;
                    throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
                }
            }
        }
    }

    return (T) instance;
}

T createAdaptiveExtension()

private T createAdaptiveExtension() {
    try {
        //获取扩展类的实例
        T instance = (T) getAdaptiveExtensionClass().newInstance();
        //初始化前处理
        instance = postProcessBeforeInitialization(instance, null);
        //注入依赖对象
        injectExtension(instance);
        //初始化后处理
        instance = postProcessAfterInitialization(instance, null);
        initExtension(instance);
        return instance;
    } catch (Exception e) {
        throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
    }
}
//获取自适应扩展类
private Class<?> getAdaptiveExtensionClass() {
    //获取扩展类。从 META-INF/dubbo/com.shopping.demo24.dubbo.spi.DemoSpi配置文件中加载
    getExtensionClasses();
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    //创建自适应扩展类
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

getExtensionClasses()->loadExtensionClasses()

private Map<String, Class<?>> loadExtensionClasses() throws InterruptedException {
    checkDestroyed();
    //缓存扩展名
    cacheDefaultExtensionName();
    Map<String, Class<?>> extensionClasses = new HashMap<>();
    //用不同的加载策略加载
    for (LoadingStrategy strategy : strategies) {
        loadDirectory(extensionClasses, strategy, type.getName());
        // compatible with old ExtensionFactory
        if (this.type == ExtensionInjector.class) {
            loadDirectory(extensionClasses, strategy, ExtensionFactory.class.getName());
        }
    }

    return extensionClasses;
}

从SPI注解上获取默认的扩展名

private void cacheDefaultExtensionName() {
    //获取SPI注解
    final SPI defaultAnnotation = type.getAnnotation(SPI.class);
    if (defaultAnnotation == null) {
        return;
    }
    String value = defaultAnnotation.value();
    //获取注解的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中
            cachedDefaultName = names[0];
        }
    }
}

生成自适应扩展类的源码,然后编译。生成的源码在上文中已经展示。生成代码时,会传入cachedDefaultName参数,生成的代码中会通过 url 中的参数找 扩展名,如果url中没有传参数,那么默认的参数就是 cachedDefaultName。

private Class<?> createAdaptiveExtensionClass() {
    // Adaptive Classes' ClassLoader should be the same with Real SPI interface classes' ClassLoader
    ClassLoader classLoader = type.getClassLoader();
    try {
        if (NativeUtils.isNative()) {
            return classLoader.loadClass(type.getName() + "$Adaptive");
        }
    } catch (Throwable ignore) {

    }
    //生成源码,此时cachedDefaultName为 d2
    String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
    //获取编译器
    org.apache.dubbo.common.compiler.Compiler compiler = extensionDirector.getExtensionLoader(
        org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    //编译并加载    
    return compiler.compile(type, code, classLoader);
}

源码生成

AdaptiveClassCodeGenerator

public String generate(boolean sort) {
    //检查是否有自适应
    // no need to generate adaptive class since there's no adaptive method found.
    if (!hasAdaptiveMethod()) {
        throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
    }

    StringBuilder code = new StringBuilder();
    //生成包信息
    code.append(generatePackageInfo());
    //导入包
    code.append(generateImports());
    //生成类声明 public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
    code.append(generateClassDeclaration());

    Method[] methods = type.getMethods();
    if (sort) {
        Arrays.sort(methods, Comparator.comparing(Method::toString));
    }
    for (Method method : methods) {
        code.append(generateMethod(method));
    }
    code.append('}');

    if (logger.isDebugEnabled()) {
        logger.debug(code.toString());
    }
    return code.toString();
}

2.4 Activate注解的匹配

如果注解中value为空,直接返回true,如果不为空,则需要与url中的参数匹配

keyValue为注解中value的值中的value,realValue为通过注解中value的值中的key在url中获取的值

private boolean isActive(String[][] keyPairs, URL url) {
    if (keyPairs.length == 0) {
        return true;
    }
    for (String[] keyPair : keyPairs) {
        // @Active(value="key1:value1, key2:value2")
        String key;
        String keyValue = null;
        if (keyPair.length > 1) {
            key = keyPair[0];
            keyValue = keyPair[1];
        } else {
            key = keyPair[0];
        }

        String realValue = url.getParameter(key);
        if (StringUtils.isEmpty(realValue)) {
            realValue = url.getAnyMethodParameter(key);
        }
        if ((keyValue != null && keyValue.equals(realValue)) || (keyValue == null && ConfigUtils.isNotEmpty(realValue))) {
            return true;
        }
    }
    return false;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值