dubbo源码解读——SPI机制

    技术2024-11-02  25

    先来了解一下java中的spi机制

    jdk中的spi

    SPI简介 一种策略模式,通过在META-INF/services/包下定义接口命名的文件,来决定使用哪个实现 调用过程

    public class Main { public static void main(String[] args) { System.out.println("---加载接口--"); ServiceLoader<SpiService> serviceLoader = ServiceLoader.load(Log.class); Iterator<SpiService > iterator = serviceLoader.iterator(); //上面只声明了两个接口,所以这里会分别调用rmi和rest的实现类的sayhello方法 while (iterator.hasNext()) { SpiService s1= iterator.next(); s1.sayHello } System.out.println("---运行结束---"); } }

    spi 全称为(Service Provider Interface),是JDK内置的一种服务提供机制。 这个是针对厂商或者插件的。 一般来说对于未知的实现或者对扩展开放的系统,通常会把一些东西抽象出来,抽象的各个模块往往有很多不同的实现方案,例如:日志模块、xml解析模块、jdbc模块等。 SPI约定 当服务的提供者,提供了服务接口的一种实现之后,在jar包中META-INF/services目录里同时创建一个以服务接口命名的文件。 该文件里就是实现该服务接口的具体实现类(全称)。 而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。

    在日志实现中可以使用

    dubbo中的spi实现

    dubbo中的spi实现更加人性化,可以支持自定义标签中的指定属性决定实现类的读取 比如这个如在均衡的策略

    dubbo中的api的使用流程

    凡是dubbo中, 接口上有 @SPI标注的,都表明此接口支持扩展,能以标签中属性的方式进行配置扩展 类似这个负载的接口

    @SPI("random") public interface LoadBalance { @Adaptive({"loadbalance"}) <T> Invoker<T> select(List<Invoker<T>> var1, URL var2, Invocation var3) throws RpcException; }

    下面我们自己来写一个负载的扩展类 1、引入jar依赖

    <!-- dubbo支持 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.5.7</version> <scope>provided</scope> </dependency>

    2、编写实现类 3、resource下新建类全路径名文件,为每个实现分配一个key 4、在消费端,便可使用上面步骤中定的实现策略(以key指代)

    dubbo中SPI机制源码解读

    在运行的时候会通过一个叫做ExtensionLoader的加载器来进行dubbo的扩展点加载 拿消费端的Reference标签举例, 通过看dubbo解析自定义标签的源码

    this.registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));

    我们知道dubbo是将reference标签的内容封装到ReferenceBean里的,打开ReferenceBean

    public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean {}

    打开父类ReferenceConfig,可以看到创建代理对象时的这一句核心代码

    this.invoker = refprotocol.refer(this.interfaceClass, (URL)this.urls.get(0));

    而这个refprotocol则是个ReferenceConfig的静态变量 (也许您会好奇,每一个reference标签对应一个ReferenceConfig对象,那在这里的协议使用类变量,会不会导致每个对象只有一个协议呢?这里就牵扯到dubbo的ExtensionLoader加载器了)

    //getExtensionLoader获取加载器 getAdaptiveExtension获取代理类 private static final Protocol refprotocol = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

    getExtensionLoader(Protocol.class)为protocol接口生成一个加载器 getAdaptiveExtension(),使用加载器生成一个代理对象---- protocol接口对象 代理对象执行时,根据参数(扩展名extName)选择实际对象

    最后的效果: 每个接口扩展点----- 对应一个ExtensionLoader加载器,内部是一个currenthashmap 如: protocol -------------- ExtensionLoader实例< protocol> filter -------------- ExtensionLoader实例< filter > loadbalance -------------- ExtensionLoader实例< loadbalance >

    代理对象是实时生成的,包括class文件,当然只生成接口中有@Adaptive注解的方法

    a、dubbo启动加载实现类时,以 key-实例 方式map缓存各个实现类 b、实际调用时,通过key --取实现需要那个实现 c、调用的发生,由生成的代理对象的来发起,最终是从URL总线中,找出extName值, extName做为key,在缓存map中取出正确的实现实现类

    Processed: 0.011, SQL: 9