一 、dubbo 源码分析之 dubbo spi

dubbo spi

JDK SPI

spi全称英文是service provider Interface,翻译成中文也就是服务提供接口,在jdk 1.6开始,就已经提供了SPI.它的使用比较简单。即在项目的类路径下提供一个META/services/xx文件,配置一个文件,文件名为接口的全路径的名称,内容为具体的实现类全路径名。jdk将会使用ServiceLoader.load()方法去解析和加载接口和其中的实现类,按需执行不同的方法。

缺点:

1.无法按需加载。虽然 ServiceLoader 做了延迟载入,使用了LazyIterator,但是基本只能通过遍历全部获取,接口的实现类得全部载入并实例化一遍。如果你并不想用某些实现类,或者某些类实例化很耗时,它也被载入并实例化了,假如我只需要其中一个,其它的并不需要这就形成了一定的资源消耗浪费

2.不具有IOC的功能,假如我有一个实现类,如何将它注入到我的容器中呢,类之间依赖关系如何完成呢?

3.serviceLoader不是线程安全的,会出现线程安全的问题

dubbo spi

dubbo在原有的spi基础上主要有以下的改变:

  1. 配置文件采用键值对配置的方式,使用起来更加灵活和简单

  2. 增强了原本SPI的功能,使得SPI具备ioc和aop的功能

dubbo spi 相关注解
  1. @SPI 标记一个spi接口 ,@SPI注解中的value是默认值

  2. @Adaptive 注解可以实现扩展类的自适应属性。可以标记在类、接口、枚举类和方法上。

    @Adaptive注解标注在接口时,首先 Dubbo 会为拓展接口生成具有代理功能的代码。然后通过javassist 或 jdk 编译这段代码,得到代理 Class 类。最后再通过反射创建代理实例。注意: 当标接口方法上时接口参数列表中需要有URL 参数。

    @Adaptive注解标注在上不会生成代理类。被标注的扩展类将直接作为默认实现类来调用方法。在Dubbo框架中类级别的仅有AdaptiveExtensionFactoryAdaptiveCompile两个类。

spi 优先级:

第一优先级@Adaptive 标注再实现类上,改实现类直接为SPI 的扩展配置。

第二优先级 @Adaptive 标注在接口方法中,且注解中配置了key键@Adaptive({key1}) ,URL路由规则中存在key1 键值对 如:URL.valueOf(“dubbo://0.0.0.0:6666/test?key1=B&simple.echo=B”)

第三优先级 @Adaptive 标注在接口方法中,注解中无key键配置,URL 路由规则中包含接口名称 (驼峰命名转为 . 分隔)键值对URL.valueOf(“dubbo://0.0.0.0:6666/test?simple.echo=B”)

第四优先级 @SPI 中的默认配置 如 @SPI(“A”)

简单实用示例

在META-INF 目录下新建 dubbo.internal 目录。在目录中创建以接口全路径为文件名的文件,文件内容为 key=实现类的全路径名

如下图所示:

在这里插入图片描述

代码如下所示:

@SPI
public interface SimpleEcho {

    void printA(String s);

    //@Adaptive({"key1","key2"})
    @Adaptive
    void printB(URL url,String s);
}
//@Adaptive
public class SimpleEchoImplA implements SimpleEcho{
    @Override
    public void printA(String s) {
        System.out.println("SimpleEchoImplA.printA "+s);
    }

    @Override
    public void printB(URL url,String s) {
        System.out.println("SimpleEchoImplA.printB "+s);
    }
}

public class SimpleEchoImplB implements SimpleEcho{
    @Override
    public void printA(String s) {
        System.out.println("SimpleEchoImplB.printA "+s);
    }

    @Override
    public void printB(URL url, String s) {
        System.out.println("SimpleEchoImplB.printB "+s);
    }
}

public class SimpleEchoImplC implements SimpleEcho{
    @Override
    public void printA(String s) {
        System.out.println("SimpleEchoImplC.printA "+s);
    }

    @Override
    public void printB(URL url, String s) {
        System.out.println("SimpleEchoImplC.printA "+s);
    }
}

public class TestMain {
    public static void main(String[] args) {
        SimpleEcho simpleEcho = ExtensionLoader.getExtensionLoader(SimpleEcho.class).getAdaptiveExtension();
        //simpleEcho.printB(URL.valueOf("dubbo://0.0.0.0:6666/test?key1=B&simple.echo=A"),"TEST");
        simpleEcho.printB(URL.valueOf("dubbo://0.0.0.0:6666/test?simple.echo=B"),"TEST");
        simpleEcho.printA("AAAA");
        //simpleEcho.printB(URL.valueOf("dubbo://0.0.0.0:6666/test"),"TEST");
    }
}

@Adaptive 标注在接口方法上生成的代理类:

生成代理类的源码位于ExtensionLoader.createAdaptiveExtensionClassCode 方法中 。

生成代码后,会默认使用 javassist 技术将代码编译、加载到jvm 中,生成对象实例

在动态生成代理的逻辑中,我们可以看到其中是使用到了 dubbo spi

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KPBhVyDC-1641886138839)(image/image_1.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dbqvM7t6-1641886138839)(image/image_2.png)]

动态生成的代理类

//代理类
package com.jufeng.learn.other.api.dubbo.spi.demo03;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class SimpleEcho$Adaptive implements com.jufeng.learn.other.api.dubbo.spi.demo03.SimpleEcho {
  public void printA(java.lang.String arg0) {
    throw new UnsupportedOperationException("method public abstract void com.jufeng.learn.other.api.dubbo.spi.demo03.SimpleEcho.printA(java.lang.String) of interface com.jufeng.learn.other.api.dubbo.spi.demo03.SimpleEcho is not adaptive method!");
  }
  public void printB(com.alibaba.dubbo.common.URL arg0, java.lang.String arg1) {
    if (arg0 == null) throw new IllegalArgumentException("url == null");
    com.alibaba.dubbo.common.URL url = arg0;
    String extName = url.getParameter("simple.echo");
    if(extName == null) throw new IllegalStateException("Fail to get extension(com.jufeng.learn.other.api.dubbo.spi.demo03.SimpleEcho) name from url(" + url.toString() + ") use keys([simple.echo])");
    com.jufeng.learn.other.api.dubbo.spi.demo03.SimpleEcho extension = (com.jufeng.learn.other.api.dubbo.spi.demo03.SimpleEcho)ExtensionLoader.getExtensionLoader(com.jufeng.learn.other.api.dubbo.spi.demo03.SimpleEcho.class).getExtension(extName);extension.printB(arg0, arg1);
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值