dubbo 源码分析 一 - 扩展点机制(SPI)

dubbo 源码分析一 - 扩展点机制

问题由来

实现相同的功能通常可以使用不同的方案:
dubbo 代理生成的方案:
有人喜欢用 JDK 动态代理,有人喜欢用 javassist 生成字节码的方式来生  成代理
RPC 协议:
可以使用 dubbo 协议,也可以使用 RMI 协议
因此 dubbo 不能将实现方案写死在代码里,需要做到在运行时根据用户的需要自由切换或扩展
*dubbo 想达到的效果是根据用户传入的参数来自动找到合适的方案*

解决过程

   if(参数 == dubbo){
        return new dubboProtocol();
   } else if(参数 == rmi){
        return new rmiProtocol();
    }
优点:可以根据用户不同的参数,返回相应的协议
缺点:如果哪天出来一个牛逼的 RPC 协议叫做 NBProtocol,需要修改源代码,添加elseif代码块。
Java设计原则是对修改关闭的,如果不写elseif怎么解决?
使用设计模式策略模式:
对外提供统一的接口,启动时把所有的接口都扫描进来
策略模式维护了一个map:
name className
dubbo com.xx.xx.DubboProtocol
rmi com.xx.xx.RMIProtocol
nb com.xx.xx.NBPRrotocol

如果用户传一个参数叫 “rmi”,通过 map.get(“rmi”) 就能得到 RMIProtocol
去哪扫描NBPRrotocol:强制规定扩展的协议必须放在 xxx.xxx 这个 classpath 下(不放不加载)。
dubbo 根据 java SPI 机制的思想实现了自己的扩展点机制

javaSPI

 javaSPI:java SPI 是 JDK 内置的一种扩展点发现机制:为某个接口寻找服务实现的机制。
 javaSPI约定:当服务提供者提供了服务接口的一种实现之后,在 jar 包的 META-INF/services 目录里同时创建一个以服务接口全限定名命名的文件,文件内容为服务接口的具体实现类全限定名,当外部程序装配这个模块的时候,就能通过该 jar 包 META-INF/services 里的配置文件找到具体的实现类名,并通过反射进行装载实例化,完成模块的注入。 
 注意:
      1.如果有多个服务实现,文件中每一行写一个服务实现
      2.号后面为注释内容
      3.文件只能够以 UTF-8 编码
 JDK 提供了一个服务实现查找的工具类:java.util.ServiceLoader 

dubbo 源码分析 一 - 扩展点机制(SPI)_第1张图片

/**
Main
*/
public class Main {
    private static ServiceLoader commands = ServiceLoader.load(Command.class);
    public static void main(String[] args) {
       for(Command command : commands){
            command.execute();
       }
    }
}
/**
Command接口
*/
public interface Command {
    void execute();
}
/**
*/
public class StartCommand implements Command {
    public void execute() {
        System.out.println("start.....");
    }
}
/**
*ShutDownCommand 
*/
public class ShutDownCommand implements Command{
    public void execute() {
        System.out.println("shutdown.....");
    }
}
/**
*com.lijc.spi.Command被扫描的位置
*/
com.lijc.spi.impl.StartCommand
com.lijc.spi.impl.ShutDownCommand

dubbo SPI

dubbo会依次从下面三个路径加载扩展点机制
META-INF/dubbo/internal(放置 dubbo 内部实现的各种扩展文件)
META-INF/dubbo/
META-INF/services/
文件名:接口全限定名
内    容:扩展点实现名=扩展实现类全限定名(多个实现类用换行符分隔)
dubbo 提供的扩展点加载工具类:com.alibaba.dubbo.common.extension.ExtensionLoader
ExtensionLoader 是 dubbo 内部的 SPI 实现,dubbo 的所有 SPI 组件都通过 ExtensionLoader 获取
解析 ExtensionLoader 的加载过程:
以 Protocol 接口实现类的加载为例

dubbo 源码分析 一 - 扩展点机制(SPI)_第2张图片

dubbo 中 SPI 接口必须以 @SPI 注解声明

     dubbo 源码解析:
     在 dubbo-rpc-api 模块的 META-INF/dubbo/internal/ 目录下,会发现 com.alibaba.dubbo.rpc.Protocol 文件,其内容为:
- Protoco文件
     filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
     listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
     mock=com.alibaba.dubbo.rpc.support.MockProtocol
    而 dubbo-rpc-default 模块的 META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol  文件内容为:
 dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
 - Protocol 接口的实现类:
    ProtocolFilterWrapper
    ProtocolListenerWrapper
    MockProtocol
    DubboProtocol
ExtensionLoader 如何加载 Protocol 接口的这些实现类?
         ServiceConfig:
         private static final Protocol protocol = ExtensionLoader.getExtensionLoder(Protocol.class).getAdaptiveExtension(); 
         getAdaptiveExtension() :
     在调用 ExtensionLoader.getExtensionLoader(Proctol.class) 之后,不是直接返回某个扩展点,而是调用 getAdaptiveExtension() 方法来获取一个扩展<\n>的适配类,这是为什么呢?因为一个扩展点有多个具体扩展的实现,所以直接通过 getExtensionLoader() 方法直接返回一个扩展是不可靠的,需要一个适配器来根据实际情况返回具体的扩展实现
     ExtensionLoader 类中的 EXTENSION_LOADERS 字段
     private static final ConcurrentMap,ExtensionLoader> EXTENSION_LOADER = new ConcurrentMap<~>();
     EXTENSION_LOADERS 用于存储 Class 对象和 ExtensionLoader 实例的映射关系

ExtensionLoader 类中的 getExtensionLoader() 方法
dubbo 源码分析 一 - 扩展点机制(SPI)_第3张图片
说明:该方法会为每一个 Class 对象创建一个 ExtensionLoader 实例, 并缓存到 EXTENSION_LOADERS 中
ExtensionLoader 类中的 cachedAdaptiveInstance 字段
private final HoldercachedAdapterInstance = new Holder();
cachedAdaptiveInstance 用于存储适配器类
ExtensionLoader 类中的 getAdaptiveExtension() 方法
dubbo 源码分析 一 - 扩展点机制(SPI)_第4张图片

自扩展Protocol步骤

  1. 新建一个 Maven 项目
  2. 引入 dubbo jar 文件
  3. 创建一个类 NBProtocol,实现 Protocol 接口
  4. 在 resources 目录下创建 META-INF/services 目录
  5. 在 META-INF/services 目录中创建名为 com.alibaba.dubbo.rpc. Protocol 的文件
  6. 文件内容:nb=com.yangsh.protocol.impl. NBProtocol

若有好的建议或疑惑之处可及时留言以共同探讨进步

你可能感兴趣的:(dubbo源码)