`

2、Dubbo基于SPI思想之Dubbo SPI实现

 
阅读更多

dubbo 

SPI接口定义

  dubbo如何定义SPI

 

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})

public @interface SPI {

       /**
       * 缺省扩展点名。
       */

       String value() default ""; //指定默认的扩展点

}

这样只有在接口上标注@SPI注解的接口类才会去查找扩展点实现

他定义了在下面几个目录查找

如下

我们以Protocol接口为例, 接口上打上SPI注解,默认扩展点名字为dubbo

@SPI("dubbo")

public interface Protocol{

     int getDefaultPort();

     @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;

    void destroy();

}

 

dubbo中内置实现了各种协议如:DubboProtocol InjvmProtocolHessianProtocol WebServiceProtocol等等

 

Dubbo默认rpc模块默认protocol实现DubboProtocol,key为dubbo

 

其他实现的协议类似,

 

我们接下来细讲Extensionloader类

 

1、ExtensionLoader.getExtensionLoader(Protocol.class)

每个定义的spi的接口都会构建一个ExtensionLoader实例,存储在

ConcurrentMap<Class<?>,ExtensionLoader<?>> EXTENSION_LOADERS 这个map对象中

 

2、loadExtensionClasses 读取扩展点中的实现类 先读取SPI注解的value值,有值作为默认扩展实现的key依次读取路径下的文件

 

3、 loadFile逐行读取com.alibaba.dubbo.rpc.Protocol文件中的内容,每行内容以key/value形式存储的

  判断类实现(如:DubboProtocol)上有没有打上@Adaptive注解,如果打上了注解,将此类作为Protocol协议的设配类缓存起来,读取下一行;否则适配类通过javasisit修改字节码生成,关于设配类功能作用后续介绍

如果类实现没有打上@Adaptive, 判断实现类是否存在入参为接口的构造器(就是DubbboProtocol类是否还有入参为Protocol的构造器),有的话作为包装类缓存到此ExtensionLoader的Set<Class<?>>集合中,这个其实是个装饰模式

如果即不是设配对象也不是wrapped的对象,那就是扩展点的具体实现对象

查找实现类上有没有打上@Activate注解,有缓存到变量cachedActivates的map中

将实现类缓存到cachedClasses中,以便于使用时获取。

4、 获取或者创建设配对象getAdaptiveExtension如果cachedAdaptiveClass有值,说明有且仅有一个实现类打了@Adaptive, 实例化这个对象返回如果cachedAdaptiveClass为空, 创建适配类字节码。

为什么要创建适配类,一个接口多种实现,SPI机制也是如此,这是策略模式,但是我们在代码执行过程中选择哪种具体的策略呢。Dubbo采用统一数据模式com.alibaba.dubbo.common.URL(它是dubbo定义的数据模型不是jdk的类),它会穿插于系统的整个执行过程,URL中定义的协议类型字段protocol,会根据具体业务设置不同的协议。url.getProtocol()值可以是dubbo也是可以webservice, 可以是zookeeper也可以是Redis

适配类的作用是根据url.getProtocol()的值extName,去ExtensionLoader. getExtension( extName)选取具体的扩展点实现。

所以能够利用javasist生成适配类的条件

接口方法中必须至少有一个方法打上了@Adaptive注解

打上了@Adaptive注解的方法参数必须有URL类型参数或者有参数中存在getURL()方法。

下面给出createAdaptiveExtensionClassCode()方法生成javasist用来生成Protocol适配类后的代码

 

import com.alibaba.dubbo.common.extension;
public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {

//没有打上@Adaptive的方法如果被调到抛异常
      public void destroy() {

throw new UnsupportedOperationException(
 "methodpublic abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of     i nterfacecom.alibaba.dubbo.rpc.Protocol is not adaptive method!")

}

 

//接口中export方法打上@Adaptive注册
      publiccom.alibaba.dubbo.rpc.Exporter export(
             com.alibaba.dubbo.rpc.Invokerarg0)  throws com.alibaba.dubbo.rpc.Invoker{
            if (arg0 == null)
                   throw newIllegalArgumentException("com.alibaba.dubbo.rpc.Invokerargument == null");
            //参数类中要有URL属性

if(arg0.getUrl() == null) 
                   throw newIllegalArgumentException( "com.alibaba.dubbo.rpc.Invokerargument getUrl() == null");
             //从入参获取统一数据模型URL

com.alibaba.dubbo.common.URL url = arg0.getUrl();
           String extName =(url.getProtocol() == null ? "dubbo" : url.getProtocol());
           //从统一数据模型URL获取协议,协议名就是spi扩展点实现类的key

if (extName == null) throw new IllegalStateException( "Fail to getextension(com.alibaba.dubbo.rpc.Protocol) name from url("  + url.toString() + ") usekeys([protocol])");

//利用dubbo服务查找机制根据名称找到具体的扩展点实现

    com.alibaba.dubbo.rpc.Protocol extension                 =(com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class)

   .getExtension(extName)

   //调具体扩展点的方法

return extension.export(arg0);
 }

 

//接口中refer方法打上@Adaptive注册
 publiccom.alibaba.dubbo.rpc.Invoker refer(Java.lang.Class arg0,
                    com.alibaba.dubbo.common.URLarg1) throws java.lang.Class {
     

//统一数据模型URL不能为空

if (arg1 == null)
            throw newIllegalArgumentException("url == null");
     

 com.alibaba.dubbo.common.URL url =arg1;

//从统一数据模型URL获取协议,协议名就是spi扩展点实现类的key

String extName = (url.getProtocol() == null ?"dubbo" : url.getProtocol());
   if (extName == null)
      thrownewIllegalStateException("Failtogetextension(com.alibaba.dubbo.rpc.Protocol)name from url("+ url.toString() + ") use keys([protocol])");


   //利用dubbo服务查找机制根据名称找到具体的扩展点实现

com.alibaba.dubbo.rpc.Protocol extension =(com.alibaba.dubbo.rpc.Protocol)  ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class)
.getExtension(extName);
    //调具体扩展点的方法

return extension.refer(arg0, arg1);

}

}

 

5、 通过createAdaptiveExtensionClassCode()生成如上的java源码代码,要被java虚拟机加载执行必须得编译成字节码,dubbo提供两种方式去执行代码的编译1)利用JDK工具类编译2)利用javassit根据源代码生成字节码。

如上图:生成Adaptive代码code利用dubbo的spi扩展机制获取compiler的设配类编译生成的adaptive代码

顺便介绍下 @Adaptive注解打在实现类上跟打在接口方法上的区别

 

1)如果有打在接口方法上,调ExtensionLoader.getAdaptiveExtension()获取适配类,会先通过前面的过程生成java的源代码,在通过编译器编译成class加载。但是Compiler的实现策略选择也是通过ExtensionLoader.getAdaptiveExtension(),如果也通过编译器编译成class文件那岂不是要死循环下去了吗?

ExtensionLoader.getAdaptiveExtension(),对于有实现类上去打了注解@Adaptive的dubbo spi扩展机制,它获取适配类不在通过前面过程生成适配类java源代码,而是在读取扩展文件的时候遇到实现类打了注解@Adaptive就把这个类作为适配类缓存在ExtensionLoader中,调用是直接返回。

6、自动Wrap上扩展点的Wrap类

这是一种装饰模式的实现,在jdk的输入输出流实现中有很多这种设计,在于增强扩展点功能。这里我们拿对于ProtocolFilterWrapper

 

 

如图Protocol继承关系ProtocolFilterWrapper, ProtocolListenerWrapper这个两个类是装饰对象用来增强其他扩展点实现的功能。ProtocolFilterWrapper功能主要是在refer 引用远程服务的中透明的设置一系列的过滤器链用来记录日志,处理超时,权限控制等等功能;ProtocolListenerWrapper在provider的exporter,unporter服务和consumer 的refer服务,destory调用时添加监听器,dubbo提供了扩展但是没有默认实现哪些监听器。

Dubbo是如何自动的给扩展点wrap上装饰对象的呢?在ExtensionLoader.loadFile加载扩展点配置文件的时候对扩展点类有接口类型为参数的构造器就是包转对象,缓存到集合中去在调ExtensionLoader的createExtension(name)根据扩展点key创建扩展的时候, 先实例化扩展点的实现, 在判断时候有此扩展时候有包装类缓存,有的话利用包转器增强这个扩展点实现的功能。如下图是实现流程

7、 IOC大家所熟知的ioc是spring的三大基础功能之一, dubbo的ExtensionLoader在加载扩展实现的时候内部实现了个简单的ioc机制来实现对扩展实现所依赖的参数的注入,  dubbo对扩展实现中公有的set方法且入参个数为一个的方法,尝试从对象工厂ObjectFactory获取值注入到扩展点实现中去。

  上图代码应该不能理解,下面我们来看看ObjectFactory是如何根据类型和名字来获取对象的,ObjectFactory也是基于dubbo的spi扩展机制的

它跟Compiler接口一样设配类注解@Adaptive是打在类AdaptiveExtensionFactory上的不是通过javassist编译生成的。

AdaptiveExtensionFactory持有所有ExtensionFactory对象的集合,dubbo内部默认实现的对象工厂是SpiExtensionFactory和SpringExtensionFactory,他们经过TreeMap排好序的查找顺序是优先先从SpiExtensionFactory获取,如果返回空在从SpringExtensionFactory获取。

SpiExtensionFactory工厂获取要被注入的对象,就是要获取dubbo spi扩展的实现,所以传入的参数类型必须是接口类型并且接口上打上了@SPI注解,返回的是一个设配类对象。

SpringExtensionFactory,Dubbo利用spring的扩展机制跟spring做了很好的融合。在发布或者去引用一个服务的时候,会把spring的容器添加到SpringExtensionFactory工厂集合中去, 当SpiExtensionFactory没有获取到对象的时候会遍历SpringExtensionFactory中的spring容器来获取要注入的对象。


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 大小: 75 KB
分享到:
评论

相关推荐

    Dubbo源码分析之SPI

    2. **AdaptiveExtension**:适应性扩展是Dubbo SPI的一大特色,它能根据当前环境动态生成适配的代码,实现服务调用的自动适配。这极大地提高了代码的可维护性和可扩展性。 3. **MetadataReport**:元数据报告接口,...

    36_SPI是啥思想?dubbo的SPI机制是怎么玩儿的?.zip

    2. 基于注解的元数据:Dubbo允许在实现类上添加`@Adaptive`注解来声明适应性扩展,这意味着可以根据特定条件(如URL、协议等)动态选择实现。 3. 自定义加载逻辑:开发者可以自定义加载策略,通过实现`spi....

    dubbo-spi_mouse1oc_dubbospi_

    Dubbo SPI(Service Provider Interface)是阿里巴巴开源的分布式服务框架Dubbo中的一个重要特性,它提供了一种服务发现和服务实现动态加载的方式。通过SPI机制,Dubbo可以灵活地扩展其功能,使得开发者能够按照自己...

    dubbo的SPI-扩展点机制

    dubbo的核心思想就是SPI思想,想读懂dubbo源码应该认真的看下,详细的讲解了dubbo的SPI思想,从为什么,是什么,怎么做的来分析了dubbo的spi思想

    dubbo spi可扩展机制源码解析

    Dubbo SPI(Service Provider Interface)是阿里巴巴开源的Dubbo框架中的一个重要特性,它提供了一种动态发现服务提供者和加载实现类的机制,使得服务消费者无需关心服务提供者的具体实现,增强了系统的可扩展性和...

    1.为什么dubbo要自己设计一套SPI.txt

    - Dubbo实现了基于构造函数的依赖注入,使得SPI服务实例的创建和管理变得更加灵活。例如,`wrapperClass.getConstructor(type).newInstance(instance)`这样的代码可以自动完成服务实例的初始化工作,减少了手动实例...

    服务治理中间件dubbo原理解析

    服务治理中间件Dubbo是阿里巴巴基于开源思想使用Java语言实现的高性能RPC框架,它支持服务的注册与发现、负载均衡、容错处理等服务治理功能。Dubbo采用微内核架构加插件体系的设计模式,通过SPI(Service Provider ...

    Dubbo SPI 机制 代码实现,dubbo-2.6.0 源码

    dubbo-2.6.0 源码

    深度解析Dubbo的可扩展机制SPI源码:从理论到实践,打造高效、稳定的分布式服务框架.rar

    Dubbo则在其之上构建了自己的SPI机制,以满足更高级别的服务治理需求。 Dubbo的SPI机制主要由以下几个关键组件构成: 1. **配置文件**:Dubbo的扩展点配置通常保存在`META-INF/dubbo.properties`或`META-INF/...

    深度解析Dubbo的可扩展机制SPI源码:从理论到实践,打造高效、稳定的分布式服务框架

    Dubbo的可扩展机制SPI(Service Provider Interface)是其核心特性之一,允许开发者根据需要动态扩展服务。SPI机制使得Dubbo作为一个高度可定制化的RPC框架,可以轻松地添加新的服务协议、序列化方式等组件。下面...

    dubbo-spiDemo:精神病

    SPI机制是基于Java的ServiceLoader类实现的,是Java标准扩展的一种方式,用于解耦框架核心与扩展模块之间的依赖。在本项目"Dubbo-spiDemo"中,我们将深入探讨这个机制及其在实际应用中的演示。 首先,我们需要理解...

    incubator-dubbo-dubbo-2.6.1

    1. **RPC框架**:Dubbo支持基于接口的透明远程调用,使得服务消费方无须关心服务提供方的具体实现。 2. **服务注册与发现**:Dubbo通过注册中心进行服务的注册和发现,服务提供方将服务信息注册到注册中心,服务消费...

    dubbo资源 dubbo-admin dubbo demo

    【压缩包子文件的文件名称列表】"dubbo-master"通常表示这是Dubbo项目的主分支或者完整版本,可能包含了Dubbo的所有模块,包括核心库、服务治理模块、协议支持、SPI扩展机制、以及相关的示例和文档。 深入讲解这些...

    【JAVA分布式系列】dubbo

    Dubbo支持多种序列化协议,如Hessian2、FastJson等,以提高通信效率。同时,Dubbo提供了多种负载均衡策略,如随机、轮询、最少活跃调用数等,以确保请求均匀分布到各个服务实例,避免单点压力过大。 再者,Dubbo...

    dubbo-thought.7z

    这意味着在没有明确指定实现类的情况下,Dubbo SPI能够根据上下文自动选择最合适的实现。 在项目"**dubbo-thought.7z**"中,可能包含了以下内容: 1. 详细的Java SPI和Dubbo SPI的原理介绍。 2. 测试代码:这些代码...

    dubbo-master.zip

    7. `dubbo-spi`:Dubbo的扩展机制,基于Java SPI(Service Provider Interface)实现,允许用户自定义实现Dubbo的组件。 【标签】中的"dubbo"和"dubbo-master"表明这是一个关于Dubbo核心源码的压缩包,包含了项目的...

    dubbo源码解析2

    ### Dubbo源码解析2 #### 一、源码阅读路径 在开始深入解析Dubbo源码之前,首先需要明确的是,Dubbo虽然代码量不算庞大,但是它涉及的技术领域非常广泛,对于初学者来说,可能需要具备一定的前置知识才能更好地...

    03-05-11-Apache Dubbo 进阶之内核剖析md1

    ### Apache Dubbo 进阶之内核剖析:深入SPI机制 #### 一、引言 Apache Dubbo是一款高性能、轻量级的开源微服务框架,它提供了面向接口的远程方法调用设计,使得开发者能够像调用本地方法一样调用远程方法。Dubbo...

Global site tag (gtag.js) - Google Analytics