本文基于dubbo2.7.7对如下三个问题分析

  1. @SPI注解的作用
  2. @Adaptive注解的作用,放在Type和Method上的区别和注意点
  3. 什么时候动态生成和编译xxx$Adaptive代码

@SPI注解

  1. dubbo中自定义了SPI机制,将接口和具体实现类分开,一个SPI接口可能有多种实现,用户也可以通过SPI实现自己的扩展。在META-INF/dubbo/internalMETA-INF/dubboMETA-INF/services三个目录下选择一个,新建名为接口全类名的文件,并在文件中填写扩展名extName和具体实现类, 扩展名任意, 但不要重复.
  2. 注意: dubbo在扫描SPI的实现类时内部是通过Map保存,无法保证顺序. 但可以按照如下顺序,META-INF/dubbo/internalMETA-INF/dubboMETA-INF/services 优先级从高到低使用相同扩展名, 覆盖默认实现。

    dubbo通过ExtensionLoader来加载这些实现类。dubbo的spi接口必须要加上的@SPI注解, 否则在加载时会抛出异常:

    @SPI注解上的value值用于指定默认实现的名字, 当加载实现类之前, 会先从接口的@SPI注解上获取value值,如果未指定,调用ExtensionLoader.getExtensionLoader(CustomInterface.class).getDefaultExtension()返回的是null

@Adaptive注解

ExtensionLoader#getAdaptiveExtension();是dubbo源码中使用比较多的方案,主要用于在spi多个实现类中找一个数据合适的扩展实现。在dubbo中该方法返回的实现类可以理解为是spi接口的一个代理类,是调用其它实现类的入口。
@Adaptive注解则是用来帮助ExtensionLoader#getAdaptiveExtension();判断要获取哪个实现类.

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {String[] value() default {};
}
  1. value指定的值只有在@Adaptive放在interface的method上时才会有作用, 放在具体实现类class上时根本不会读取该value的值
  2. value的值即为key名称,dubbo会通过key名称查找对应的扩展名extName,例如
    String extName = url.getParameter(“transporter”, “netty”),
    但对部分spi接口做了特殊处理,例如Protocol,不是调用getParameter()方法获取extName, 如下:
    String extName = (url.getProtocol() == null ? “dubbo” : url.getProtocol());
  3. value可以指定多个,按照数组顺序, 优先级从高到底, 直到查到为止.
    如果没有找到value中对应的扩展, 则使用接口的SimpleName, 以驼峰处添加点分隔符,字符串全部转成小写,作为参数获取获取extName例如org.apache.dubbo.xxx.YyyInvokerWrapper, 转成 yyy.invoker.wrapper
  4. 调用ExtensionLoader#getExtension(java.lang.String extName)方法获取该扩展名对应的具体实例对象,未获取到则报错

@Adaptive注解定义可以看出既可以放在类上, 又可以放在方法上. 但却有着不同的意义和响应的限制

用在SPI实现类上作用及注意点

  1. 只能放在实现类上,放在spi接口上不会被dubbo处理,无意义;
  2. 如果多个实现类有@Adaptive注解, 会更具SPI目录的优先级覆盖;但同在META-INF/dubbo/internal目录下,同类名的话则报错;如果已经有一个带有@Adaptive注解的实现类,再调用ExtensionLoader#addExtension则报错.
    这样设计的原因其实很简单:只能有一个实现类带有@Adaptive注解, dubbo内部带有@Adaptive注解的实现类又希望可以被用户覆盖.
  3. 放在实现类上时,则不会再动态生成xxx$Adaptive类。实际上dubbo源码中也就是org.apache.dubbo.common.compiler.support.AdaptiveCompilerorg.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory这样使用, 其它都是放在SPI接口方法上.

用在SPI接口方法上作用及注意点

  1. 放在SPI接口的方法上@Adaptive注解中的value属性,才会被处理生效
  2. 动态生成xxx$Adaptive类,动态编译class,例如:org.apache.dubbo.rpc.Protocol, 则生成Protocol$Adaptive源码并编译成class
  3. 当实现类和接口都不存在@Adaptive时,调用#getAdaptiveExtension();则会报如下异常:

什么时候动态生成$Adaptive代码

ExtensionLoader在从META-INF/dubbo/internalMETA-INF/dubboMETA-INF/services三个目录加载SPI实现类时,如果实现类上存在@Adaptive注解,则会被缓存到成员变量cachedAdaptiveClass,当调用ExtensionLoader#getAdaptiveExtension()会先判断实例是否存在,不存在则通过cachedAdaptiveClass创建对象,如果cachedAdaptiveClass的仍然为空,则更具SPI接口动态创建xxx$Adaptive代码,xxx代表具体的SPI接口。此时要求接口中必须有@Adaptive注解的方法, 否则报错. 参见源码:org.apache.dubbo.common.extension.AdaptiveClassCodeGenerator


org.apache.dubbo.common.extension.AdaptiveClassCodeGenerator#generate()

动态生成的$Adaptive代码内部是什么逻辑

对方法参数进行null值的一些常规校验, 然后根据@Adaptive注解的value属性值获取SPI扩展名和方法返回值类型获取对应的SPI实现类。

例如org.apache.dubbo.rpc.Protocol接口动态生成的Protocol$Adaptive代码如下:

package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {public java.util.List getServers()  {throw new UnsupportedOperationException("The method public default java.util.List org.apache.dubbo.rpc.Protocol.getServers() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");}public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {if (arg1 == null)throw new IllegalArgumentException("url == null");org.apache.dubbo.common.URL url = arg1;String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );if(extName == null)throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);return extension.refer(arg0, arg1);}public void destroy()  {throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");}public int getDefaultPort()  {throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");}public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {if (arg0 == null)throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");if (arg0.getUrl() == null)throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");org.apache.dubbo.common.URL url = arg0.getUrl();String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );if(extName == null)throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);return extension.export(arg0);}
}

dubbo SPI之@SPI、@Adaptive注解, 以及什么时候动态生成$Adaptive代码相关推荐

  1. Dubbo源码分析(三)Dubbo中的SPI和自适应扩展机制

    前言 我们在往期文章中,曾经深入分析过Java的SPI机制,它是一种服务发现机制.具体详见:深入理解JDK的SPI机制 在继续深入Dubbo之前,我们必须先要明白Dubbo中的SPI机制.因为有位大神 ...

  2. 【七夕特殊礼物】Dubbo学习之SPI实战与debug源码

    目录 绪论 环境搭建 dubbo-demo-interface dubbo-demo-xml dubbo-demo-xml-provider 源码跟踪 getExtension createExten ...

  3. spring-boot框架dubbo在controlle中r注解@Reference注入service,但是调用方法时候,service报null空指针异常

    spring-boot框架dubbo在controlle中r注解@Reference注入service,但是调用方法时候,service报null空指针异常 参考文章: (1)spring-boot框 ...

  4. 【原创】【SPI】SPI通信协议介绍

    1.这是个什么玩意 接上篇接着介绍,协议主要就是保证双方能够正常的通信并理解对方的"话".而本篇介绍的这个SPI协议是为了保证SPI接口的两头双方能够正常通信.具体的说,就是针对那 ...

  5. 【SPI】SPI学习之SPI硬件相关

    关联内容: [SPI]SPI学习之SPI硬件相关 [SPI]SPI学习之SPI驱动相关 [SPI]SPI学习之SPI调试相关 目录 SPI硬件知识 SPI相关的缩写或说法 SPI外部信号描述 SPI硬 ...

  6. linux内核添加spi驱动,Linux内核驱动之spi子系统spi协议.docx

    Linux内核驱动之spi子系统spi协议 概况 SPI接口是摩托罗拉首先提出的全双工三线同步串行外围接口SCK,MOSI,MISO,采用主从模式(Master Slave)架构:支持多slave模式 ...

  7. STM32L475 硬件SPI+软件SPI驱动ST7789V2

    前言 最近购买了IoT Board 潘多拉开发板来研究,学习使用STM32CubeMX工具配置SPI,然后驱动了TFTLCD.潘多拉开发板的TFTLCD驱动IC是ST7789V2,结合原子哥的TFTL ...

  8. 【SPI】SPI学习之SPI驱动相关

    关联内容: [SPI]SPI学习之SPI硬件相关 [SPI]SPI学习之SPI驱动相关 [SPI]SPI学习之SPI调试相关 目录 spi驱动框架 SPI控制器 spi_master 结构体 spi主 ...

  9. Mybatis整体学习笔记-CRUD-配置解析-结果集映射-日志-注解开发-复杂环境-动态SQL-缓存

    MyBatis 要多对官方文档进行学习 https://mybatis.org/mybatis-3/zh/index.html 简介 MyBatis 持久层框架 Dao Access Objects ...

最新文章

  1. 数据科学家所需的大脑训练
  2. Struts2官网翻译
  3. PHP扩展CURL的用法详解
  4. 怎么解决深入学习PHP的瓶颈
  5. 【DP】Rotating Substrings(CF1363F)
  6. 设计师职场提升效率品质素材网,一个足矣
  7. validatebox自定义验证规则以及使用
  8. Spring与Struts2的整合
  9. 20200703每日一句
  10. csgo自建局域网服务器,csgo怎么创建局域网游戏
  11. 解决win10中无法打开CHM文件的方法
  12. Matlab中zeros和ones函数用法
  13. 约束布局ConstraintLayout ,报错:This view is not constrained vertically
  14. 3dsmax 2019 插件开发要点
  15. 10 款值得珍藏的 Chrome 浏览器插件
  16. 使用GPG验证文件签名
  17. CentOS8 Nomad安装(2)
  18. 加密交易所的新战场:高频交易
  19. python+百度翻译api制作中英文互转的代码应用实例
  20. 常见条码碳带质量问题有哪些影响?

热门文章

  1. 闲鱼快速引流玩法3个实用分享!闲鱼自动化引流脚本!
  2. 刺激!抛弃英特尔、偷学安卓,这届苹果真香!
  3. 狂神-SpringCloud笔记-总
  4. Zookeeper分布式入门——ZK四字命令(一)
  5. 济南计算机等级考试报名时间,考生注意!山东全国计算机等级考试报名时间来了!...
  6. sip 协议中:from to 的理解
  7. (转)无效的过程调用或参数: 'MidB'
  8. php 奥运会奖牌榜历史接口
  9. 【CE】游戏内存修改 植物大战僵尸 太阳数量
  10. linux 设置环境变量