dubbo spi 的应用指南


前言

SPI全称为Service Provider Interface,是一种服务发现机制。在Dubbo中,SPI机制被广泛应用于加载和扩展各种组件,如协议(Protocol)、负载均衡(LoadBalance)、注册中心(Registry)等。Dubbo并未直接使用Java原生的SPI机制,而是对其进行了增强,以满足更复杂和灵活的需求。


Dubbo 加载扩展的整个流程如下:
在这里插入图片描述

一、配置文件目录

  • META-INF/services/:
    用途:这个目录下的SPI配置文件主要用于兼容JDK原生的SPI机制。

  • META-INF/dubbo/:
    用途:这个目录专门用于存放用户自定义的Dubbo SPI配置文件,开发应用的存放目录。格式为文件名为全路径名了,内容采用键值对(Key-Value,简称KV)的格式,其中key被称为扩展名(ExtensionName),value则是对应的具体实现类。
    例如
    目录:META-INF/dubbo/org.apache.dubbo.rpc.Protocol
    内容:http=com.sjl.HttpProtocol

  • META-INF/dubbo/internal/:
    用途:这个目录用于存放Dubbo内部使用的SPI配置文件

二、标准SPI

// "one" 是默认拓展实现类
@SPI("one")
public interface BaseSpi {

    void execute();

}

spi实现

public class OneBaseSpi implements BaseSpi{

    @Override
    public void execute() {
        System.out.println("one base spi");
    }

}
public class TwoBaseSpi implements BaseSpi{

    @Override
    public void execute() {
        System.out.println("two base spi");
    }
    
}

配置
在这里插入图片描述

one=org.sjl.spi.base.OneBaseSpi
two=org.sjl.spi.base.TwoBaseSpi

测试

@Component
public class BaseTest implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args){
        ExtensionLoader<BaseSpi> extensionLoader = ExtensionLoader.getExtensionLoader(BaseSpi.class);
        // 获取key为 one 的拓展实现类
        BaseSpi one = extensionLoader.getExtension("one");
        one.execute();
        // 获取key为 two 的拓展实现类
        BaseSpi two = extensionLoader.getExtension("two");
        two.execute();
        // 获取 spi 注解上的默认拓展实现类
        BaseSpi def = extensionLoader.getDefaultExtension();
        def.execute();
    }

}

测试结果
在这里插入图片描述

三、自适应SPI(Adaptive SPI)

1、@Adaptive说明

1.1、方法级别的注解:

  • @Adaptive 注解标记在接口方法上时,Dubbo 会在初始化扩展点时自动生成一个动态代理类,这个代理类会包含被 @Adaptive 注解修饰的方法的代理实现。这些代理实现会根据运行时传入的参数(URL中的参数)来动态选择并调用合适的实现类。URL可以直接作为入参,或者作为入参的入参如org.apache.dubbo.rpc.Invoker
  • @Adaptive 注解中的 String[] value()
    • 没有填写时,将接口名转换为小写并使用点(.)分隔,然后从 URL 中查找对应的参数值,例如org.sjl.spi.Adaptive.AdaptiveSpi,则对应的key为adaptive.spi
    • 填写时,则通过value的值作为key
      • value数组等于1
        例如@Adaptive(“ADP”)
        则从url的取值为
        url.getParameter(“ADP”);
      • value数组大于1
        例如@Adaptive(“ADP”,“DDD”)
        则从url的取值为
        url.getParameter(“ADP”, url.getParameter(“DDD”));

1.2、 类级别的注解:

  • @Adaptive 注解标记在类上时,该类通常会被直接作为自适应实现类来进行代理。

2、@Adaptive作用在类上

spi接口

@SPI("one")
public interface AdaptiveClassSpi {

    void execute(String name);

}

spi实现类

public class OneAdaptiveClassSpi implements AdaptiveClassSpi{

    public void execute(String name){
        System.out.println("One Adaptive Class Spi");
    }

}
public class TwoAdaptiveClassSpi implements AdaptiveClassSpi{

    public void execute(String name){
        System.out.println("Two Adaptive Class Spi");
    }

}

自定义代理类

@Adaptive
public class AdaptiveClassSpiProxy implements AdaptiveClassSpi {

    private static final ExtensionLoader<AdaptiveClassSpi> extensionLoader = ExtensionLoader.getExtensionLoader(AdaptiveClassSpi.class);

    public void execute(String name) {
        AdaptiveClassSpi adaptiveClassSpi = null;
        if (StringUtils.isBlank(name) || !extensionLoader.hasExtension(name)) {
            adaptiveClassSpi = extensionLoader.getDefaultExtension();
        } else {
            adaptiveClassSpi = extensionLoader.getExtension(name);
        }
        adaptiveClassSpi.execute(name);
    }

}

配置

one=org.sjl.spi.Adaptive.OneAdaptiveClassSpi
two=org.sjl.spi.Adaptive.TwoAdaptiveClassSpi
adaptive=org.sjl.spi.Adaptive.AdaptiveClassSpiProxy

测试

@Component
public class AdaptiveClassSpiTest implements ApplicationRunner {

    private static final ExtensionLoader<AdaptiveClassSpi> adaptiveClassSpi = ExtensionLoader.getExtensionLoader(AdaptiveClassSpi.class);

    @Override
    public void run(ApplicationArguments args) throws Exception {
        AdaptiveClassSpi adaptiveExtension = adaptiveClassSpi.getAdaptiveExtension();
        adaptiveExtension.execute("one");
        adaptiveExtension.execute("two");
    }

}

结果
在这里插入图片描述

3、@Adaptive作用在方法上

spi接口

@SPI("one")
public interface AdaptiveSpi {

    public static final String ADP = "ADP";

    @Adaptive(ADP)
    void execute(URL url);

}

spi实现类

public class OneAdaptiveSpi implements AdaptiveSpi{

    public void execute(URL url){
        System.out.println("One Adaptive Spi");
    }

}
public class TwoAdaptiveSpi implements AdaptiveSpi{

    public void execute(URL url){
        System.out.println("Two Adaptive Spi");
    }

}

配置

one=org.sjl.spi.Adaptive.OneAdaptiveSpi
two=org.sjl.spi.Adaptive.TwoAdaptiveSpi

测试

@Component
public class AdaptiveSpiTest implements ApplicationRunner {

    private static final ExtensionLoader<AdaptiveSpi> extensionLoader = ExtensionLoader.getExtensionLoader(AdaptiveSpi.class);

    @Override
    public void run(ApplicationArguments args) throws Exception {
    	// 获取自适应代理类 AdaptiveSpi$Adaptive
        AdaptiveSpi adaptiveExtension = extensionLoader.getAdaptiveExtension();
        URL url = buildURL();
        // 没有对应ADP参数,则取默认spi
        adaptiveExtension.execute(url);

        URL url1 = buildURL().addParameter(AdaptiveSpi.ADP, "two");
        // 取key为ADP的参数的spi
        adaptiveExtension.execute(url1);
    }

    public static URL buildURL() {
        URL url = new URL(null, "127.0.0.1", 8090);
        return url;
    }
}

生成的代理类

public class AdaptiveSpi$Adaptive implements org.sjl.spi.Adaptive.AdaptiveSpi {
    public void execute(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("ADP", "one");
        if (extName == null)
            throw new IllegalStateException("Failed to get extension (org.sjl.spi.Adaptive.AdaptiveSpi) name from url (" + url.toString() + ") use keys([ADP])");
        ScopeModel scopeModel = ScopeModelUtil.getOrDefault(url.getScopeModel(), org.sjl.spi.Adaptive.AdaptiveSpi.class);
        org.sjl.spi.Adaptive.AdaptiveSpi extension = (org.sjl.spi.Adaptive.AdaptiveSpi) scopeModel.getExtensionLoader(org.sjl.spi.Adaptive.AdaptiveSpi.class).getExtension(extName);
        extension.execute(arg0);
    }
}

结果
在这里插入图片描述

三、激活SPI(Activate SPI)

Dubbo 的激活点机制基于 @Activate 注解完成,可以用于实现根据条件加载多个 SPI 激活点实现类。

  • @Activate 注解:
    • String[] group() default {}:
      • getActivateExtension 接口传入的 group 参数为 null 或者 length==0,表示不限制 group;
      • @Activate 注解中的参数 groups 是否包含传入的限制参数 group,如果包含,则允许加载当前的 SPI 实现。
    • String[] value() default {}:
      • @Activate 注解没有 value() 属性,则默认所有值不包括空;
      • URL#getParameters() 中的一个参数名,则认为默认是允许当前的 SPI 实现加载的
    • int order() default 0;
      • 表示加载顺序,数字越小越靠前
        spi接口
@SPI
public interface ActivateSpi {

    void execute();

}

spi实现

@Activate(value = "one",group = {"active","active2"}, order = 1)
public class OneActivateSpi implements ActivateSpi{

    @Override
    public void execute() {
        System.out.println("One Activate Spi");
    }
}

@Activate(value = "two",group = "active", order = 2)
public class TwoActivateSpi implements ActivateSpi{

    @Override
    public void execute() {
        System.out.println("Two Activate Spi");
    }
}

@Activate(group = "active", order = 3)
public class ThreeActivateSpi implements ActivateSpi{

    @Override
    public void execute() {
        System.out.println("Three Activate Spi");
    }
}

@Activate(order = 4, value = "four")
public class FourActivateSpi implements ActivateSpi{

    @Override
    public void execute() {
        System.out.println("Four Activate Spi");
    }
}

配置

one=org.sjl.spi.Activate.OneActivateSpi
two=org.sjl.spi.Activate.TwoActivateSpi
three=org.sjl.spi.Activate.ThreeActivateSpi
four=org.sjl.spi.Activate.FourActivateSpi

测试

@Component
public class ActiveSpiTest implements ApplicationRunner {

    private static final ExtensionLoader<ActivateSpi> extensionLoader = ExtensionLoader.getExtensionLoader(ActivateSpi.class);

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("仅指定 group");
        String group = "active";
        // 只会获取到仅设置了group为active 且 value值不填写的
        List<ActivateSpi> activateSpiList = extensionLoader.getActivateExtension(buildURL(), new String[]{}, group);
        activateSpiList.forEach(ActivateSpi::execute);

        System.out.println("指定 group 和 value");
         // 获取到仅设置了group为active 且 value值不填写的或者value为one的
        List<ActivateSpi> activateSpiList1 = extensionLoader.getActivateExtension(buildURL(), new String[]{"one"}, group);
        activateSpiList1.forEach(ActivateSpi::execute);

        System.out.println("指定 value");
        // 获取到设置了group的所有值或者不设置  且 value需要等于four或者不填的
        List<ActivateSpi> activateSpiList2 = extensionLoader.getActivateExtension(buildURL(), new String[]{"four"}, null);
        activateSpiList2.forEach(ActivateSpi::execute);

    }

    public static URL buildURL() {
        URL url = new URL(null, "127.0.0.1", 8090);
        return url;
    }
}

测试结果
在这里插入图片描述

四、Dubbo AOP

AOP 增强,通常以Wrapper结尾,且需要实现如下协议

  • wrapper 类也必须实现 SPI 接口
  • wrapper 类必须有一个含有单个 SPI 参数的构造器

针对上面的 AdaptiveSpi ,我们增加AOP强化功能
增强类

public class AdaptiveSpiWrapper implements AdaptiveSpi {

    private AdaptiveSpi adaptiveSpi;

    public AdaptiveSpiWrapper(AdaptiveSpi adaptiveSpi){
        this.adaptiveSpi = adaptiveSpi;
    }

    @Override
    public void execute(URL url) {
        System.out.println("@AOP(order = 100)");
        adaptiveSpi.execute(url);
    }

}
public class AdaptiveSpiWrapper2 implements AdaptiveSpi {

    private AdaptiveSpi adaptiveSpi;

    public AdaptiveSpiWrapper2(AdaptiveSpi adaptiveSpi){
        this.adaptiveSpi = adaptiveSpi;
    }

    @Override
    public void execute(URL url) {
        System.out.println("@AOP(order = -100)");
        adaptiveSpi.execute(url);
    }

}

配置

  • 配置的先后顺序,代表AOP的先后顺序
one=org.sjl.spi.Adaptive.OneAdaptiveSpi
two=org.sjl.spi.Adaptive.TwoAdaptiveSpi
wrapper2=org.sjl.spi.aop.AdaptiveSpiWrapper2
wrapper=org.sjl.spi.aop.AdaptiveSpiWrapper

测试

@Component
public class AdaptiveSpiTest implements ApplicationRunner {

    private static final ExtensionLoader<AdaptiveSpi> extensionLoader = ExtensionLoader.getExtensionLoader(AdaptiveSpi.class);

    @Override
    public void run(ApplicationArguments args) throws Exception {
        AdaptiveSpi adaptiveExtension = extensionLoader.getAdaptiveExtension();
        URL url = buildURL();
        // 此时的链路为 AdaptiveSpiWrapper2 -> AdaptiveSpiWrapper -> OneAdaptiveSpi
        adaptiveExtension.execute(url);

        URL url1 = buildURL().addParameter(AdaptiveSpi.ADP, "two");
        // 此时的链路为 AdaptiveSpiWrapper2 -> AdaptiveSpiWrapper -> TwoAdaptiveSpi
        adaptiveExtension.execute(url1);
    }

    public static URL buildURL() {
        URL url = new URL(null, "127.0.0.1", 8090);
        return url;
    }
}

结果
在这里插入图片描述

五、Dubbo IOC

Dubbo IOC 的实现机制

  • setter 方法注入依赖。
  • 注入为spring bean 或者 自适应扩展点

针对上面的代码,在OneBaseSpi 依赖注入 AdaptiveSpi

public class OneBaseSpi implements BaseSpi{

    private AdaptiveSpi adaptiveSpi;

    public void setAdaptiveSpi(AdaptiveSpi adaptiveSpi) {
        this.adaptiveSpi = adaptiveSpi;
    }

    @Override
    public void execute() {
        adaptiveSpi.execute(buildURL());
        System.out.println("one base spi");
    }

    public static URL buildURL() {
        URL url = new URL(null, "127.0.0.1", 8090);
        return url;
    }

}

配置保存不变

one=org.sjl.spi.base.OneBaseSpi

测试

@Component
public class BaseTest implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args){
        ExtensionLoader<BaseSpi> extensionLoader = ExtensionLoader.getExtensionLoader(BaseSpi.class);
        BaseSpi one = extensionLoader.getExtension("one");
        one.execute();
    }

}

结果
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值