核心类

ExtensionLoader

使用方法

  1. 定义接口,使用@SPI标记
@SPI("impl1")
public interface SimpleExt {
// @Adaptive example, do not specify a explicit key.
@Adaptive
String echo(URL url, String s); @Adaptive({"key1", "key2"})
String yell(URL url, String s); // no @Adaptive
String bang(URL url, int i);
} @SPI("impl1")
public interface UseProtocolKeyExt {
// protocol key is the second
@Adaptive({"key1", "protocol"})
String echo(URL url, String s); // protocol key is the first
@Adaptive({"protocol", "key2"})
String yell(URL url, String s);
}
  1. 扩展类
  • SimpleExt.java
public class SimpleExtImpl1 implements SimpleExt {
public String echo(URL url, String s) {
return "Ext1Impl1-echo";
} public String yell(URL url, String s) {
return "Ext1Impl1-yell";
} public String bang(URL url, int i) {
return "bang1";
}
} public class SimpleExtImpl2 implements SimpleExt {
public String echo(URL url, String s) {
return "Ext1Impl2-echo";
} public String yell(URL url, String s) {
return "Ext1Impl2-yell";
} public String bang(URL url, int i) {
return "bang2";
} } public class SimpleExtImpl3 implements SimpleExt {
public String echo(URL url, String s) {
return "Ext1Impl3-echo";
} public String yell(URL url, String s) {
return "Ext1Impl3-yell";
} public String bang(URL url, int i) {
return "bang3";
} }

  • UseProtocolKeyExt.java
public class UseProtocolKeyExtImpl1 implements UseProtocolKeyExt {
public String echo(URL url, String s) {
return "Ext3Impl1-echo";
} public String yell(URL url, String s) {
return "Ext3Impl1-yell";
}
} public class UseProtocolKeyExtImpl2 implements UseProtocolKeyExt {
public String echo(URL url, String s) {
return "Ext3Impl2-echo";
} public String yell(URL url, String s) {
return "Ext3Impl2-yell";
}
} public class UseProtocolKeyExtImpl3 implements UseProtocolKeyExt {
public String echo(URL url, String s) {
return "Ext3Impl3-echo";
} public String yell(URL url, String s) {
return "Ext3Impl3-yell";
}
}

3.SPI资源路径

  • 路径-> META-INFO/dubbo/interal/{@SPI注解的全限定名}

com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt

  • 实现类配置

    • com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt

    impl1=com.alibaba.dubbo.common.extensionloader.ext1.impl.SimpleExtImpl1#Hello World impl2=com.alibaba.dubbo.common.extensionloader.ext1.impl.SimpleExtImpl2 # Comment 2 impl3=com.alibaba.dubbo.common.extensionloader.ext1.impl.SimpleExtImpl3 # with head space

    • com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt

    impl1=com.alibaba.dubbo.common.extensionloader.ext3.impl.UseProtocolKeyExtImpl1 impl2=com.alibaba.dubbo.common.extensionloader.ext3.impl.UseProtocolKeyExtImpl2 impl3=com.alibaba.dubbo.common.extensionloader.ext3.impl.UseProtocolKeyExtImpl3

4.测试方法

  • SimpleExt.java
    @Test
public void test_getAdaptiveExtension_defaultAdaptiveKey() throws Exception {
{
// #1
SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getAdaptiveExtension(); Map<String, String> map = new HashMap<String, String>();
URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
// #2
String echo = ext.echo(url, "haha");
assertEquals("Ext1Impl1-echo", echo);
} {
#3
SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getAdaptiveExtension(); Map<String, String> map = new HashMap<String, String>();
map.put("simple.ext", "impl2");
URL url = new URL("p1", "1.2.3.4", 1010, "path1", map); #4
String echo = ext.echo(url, "haha");
assertEquals("Ext1Impl2-echo", echo);
}
}
  • #1.该方法执行后会通过ExtensionLoader.createAdaptiveExtensionClassCode生成一个代理类对象ext,见附录1
  • #2.1 ext中有一行:String extName = url.getParameter("simple.ext", "impl1"); (key,defaultValue) ,其中key:simple.ext是接口名称SimpleExt去驼峰加. 构成,原因在于echo方法没有@Adaptive注解没有传入参数;defaultValue根据接口@SPI值impl1生成。
  • #2.2 url中map为空,extName取传入的默认值impl1
  • #2.3 ext中有一行 com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt extension = (com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt.class).getExtension(extName); 根据extName扩展名去寻找真正需要的扩展实现类。此时extName是impl1,那么真正执行的echo就是impl1代表的SimpleExtImpl1实例
  • #3同#1
  • #4.1 url中map为("simple.ext", "impl2")
  • #4.2 通过String extName = url.getParameter("simple.ext", "impl1");获得extName为impl2
  • #4.3 此时extName是impl2,那么真正执行的echo就是impl2代表的SimpleExtImpl2实例
    @Test
public void test_getAdaptiveExtension_customizeAdaptiveKey() throws Exception { SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getAdaptiveExtension(); Map<String, String> map = new HashMap<String, String>();
map.put("key2", "impl2");
URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
#5
String echo = ext.yell(url, "haha");
assertEquals("Ext1Impl2-yell", echo);
#6
url = url.addParameter("key1", "impl3"); // note: URL is value's type
echo = ext.yell(url, "haha");
assertEquals("Ext1Impl3-yell", echo);
}
  • #5.1 因为yell方法上@Adaptive注解有参数{"key1", "key2"},那么ext生成的代理方法中获取extName代码为:String extName = url.getParameter("key1",url.getParameter("key2", "impl1"));
  • #5.2 url中map为("key2", "impl2"),第一轮判后extName是impl2,第二轮判断后extName为impl2
  • #5.3 此时extName是impl2,那么真正执行的echo就是impl2代表的SimpleExtImpl2实例
  • #6.1 url中map为("key2", "impl2")("key1", "impl3") ,第一轮判后extName是impl2,第二轮判断后extName为impl3
  • #6.2 此时extName是impl3,那么真正执行的echo就是impl3代表的SimpleExtImpl3实例
  • 参数判断顺序与参数定义顺序相反
    @Test
public void test_getAdaptiveExtension_protocolKey() throws Exception {
#1
UseProtocolKeyExt ext = ExtensionLoader.getExtensionLoader(UseProtocolKeyExt.class).getAdaptiveExtension(); {
#2
String echo = ext.echo(URL.valueOf("1.2.3.4:20880"), "s");
assertEquals("Ext3Impl1-echo", echo); // default value #3
Map<String, String> map = new HashMap<String, String>();
URL url = new URL("impl3", "1.2.3.4", 1010, "path1", map); echo = ext.echo(url, "s");
assertEquals("Ext3Impl3-echo", echo); // use 2nd key, protocol #4
url = url.addParameter("key1", "impl2");
echo = ext.echo(url, "s");
assertEquals("Ext3Impl2-echo", echo); // use 1st key, key1
}
  • #1.该方法执行后会通过ExtensionLoader.createAdaptiveExtensionClassCode生成一个代理类对象ext,见附录2
  • #2.1 echo方法@Adaptive注解中有值({"key1", "protocol"}),且其中一个为protocol,在ext中extName判断方法为url.getParameter("key1", (url.getProtocol() == null ? "impl1" : url.getProtocol()));
  • #2.2 url中未指定protocol,同时map为null,判断后extName为默认值impl1
  • #2.3 此时extName是impl1,那么真正执行的echo就是impl1代表的UseProtocolKeyExtImpl1实例
  • #3.1 此时url的protocol为impl3,extName为impl3
  • #3.2 此时extName是impl3,那么真正执行的echo就是impl3代表的UseProtocolKeyExtImpl3实例
  • #4.1 此时url的proto是impl3,map("key1", "impl2"),extName是impl2
  • #4.2 此时extName是impl2,那么真正执行的echo就是impl2代表的UseProtocolKeyExtImpl2实例
        {

            Map<String, String> map = new HashMap<String, String>();
URL url = new URL(null, "1.2.3.4", 1010, "path1", map);
#5
String yell = ext.yell(url, "s");
assertEquals("Ext3Impl1-yell", yell); // default value #6
url = url.addParameter("key2", "impl2"); // use 2nd key, key2
yell = ext.yell(url, "s");
assertEquals("Ext3Impl2-yell", yell);
#7
url = url.setProtocol("impl3"); // use 1st key, protocol
yell = ext.yell(url, "d");
assertEquals("Ext3Impl3-yell", yell);
}
}
  • #5.1 yell方法@Adaptive注解中有值({"protocol", "key1"}),且其中一个为protocol,在ext中extName判断方法为url.getProtocol() == null ? (url.getParameter( "key2", "impl1" ) ) : url.getProtocol()
  • #5.2 url中未指定protocol,同时map为null,判断后extName为默认值impl1
  • #5.3 此时extName是impl1,那么真正执行的echo就是impl1代表的UseProtocolKeyExtImpl1实例
  • #6.1 此时map("key2", "impl2"),extName为impl2
  • #6.2 此时extName是impl2,那么真正执行的echo就是impl2代表的UseProtocolKeyExtImpl2实例
  • #7.1 此时url的proto是impl3,map("key1", "impl2"),extName是impl3
  • #7.2 此时extName是impl3,那么真正执行的echo就是impl3代表的UseProtocolKeyExtImpl3实例

附录1

package com.alibaba.dubbo.common.extensionloader.ext1;

import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class SimpleExt$Adaptive implements com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt {
public java.lang.String echo(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.ext", "impl1"); if (extName == null) {
throw new IllegalStateException(
"Fail to get extension(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt) name from url(" +
url.toString() + ") use keys([simple.ext])");
} com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt extension = (com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt.class)
.getExtension(extName); return (extension.echo(arg0, arg1));
} public java.lang.String yell(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("key1",
url.getParameter("key2", "impl1")); if (extName == null) {
throw new IllegalStateException(
"Fail to get extension(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt) name from url(" +
url.toString() + ") use keys([key1, key2])");
} com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt extension = (com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt.class)
.getExtension(extName); return (extension.yell(arg0, arg1));
} public java.lang.String bang(com.alibaba.dubbo.common.URL arg0, int arg1) {
throw new UnsupportedOperationException(
"method public abstract java.lang.String com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt.bang(com.alibaba.dubbo.common.URL,int) of interface com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt is not adaptive method!");
}
}

附录2

package com.alibaba.dubbo.common.extensionloader.ext3;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class UseProtocolKeyExt$Adaptive implements com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt {
public java.lang.String echo( 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( "key1", (url.getProtocol() == null ? "impl1" : url.getProtocol() ) );
if ( extName == null )
throw new IllegalStateException( "Fail to get extension(com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt) name from url(" + url.toString() + ") use keys([key1, protocol])" );
com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt extension =
(com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt)ExtensionLoader.getExtensionLoader( com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt.class ).getExtension( extName );
return(extension.echo( arg0, arg1 ) );
} public java.lang.String yell( 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.getProtocol() == null ? (url.getParameter( "key2", "impl1" ) ) : url.getProtocol();
if ( extName == null )
throw new IllegalStateException( "Fail to get extension(com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt) name from url(" + url.toString() + ") use keys([protocol, key2])" );
com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt extension = (com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt)ExtensionLoader.
getExtensionLoader( com.alibaba.dubbo.common.extensionloader.ext3.UseProtocolKeyExt.class ).getExtension( extName );
return(extension.yell( arg0, arg1 ) );
}
}

读Dubbo源码,学习SPI的更多相关文章

  1. Dubbo源码学习--服务是如何引用的

    ReferenceBean 跟服务引用一样,Dubbo的reference配置会被转成ReferenceBean类,ReferenceBean实现了InitializingBean接口,直接看afte ...

  2. Dubbo源码学习--注册中心分析

    相关文章: Dubbo源码学习--服务是如何发布的 Dubbo源码学习--服务是如何引用的 注册中心 关于注册中心,Dubbo提供了多个实现方式,有比较成熟的使用zookeeper 和 redis 的 ...

  3. Dubbo源码学习--服务是如何发布的

    相关文章: Dubbo源码学习--服务是如何发布的 Dubbo源码学习--服务是如何引用的 ServiceBean ServiceBean 实现ApplicationListener接口监听Conte ...

  4. Dubbo源码学习--集群负载均衡算法的实现

    相关文章: Dubbo源码学习文章目录 前言 Dubbo 的定位是分布式服务框架,为了避免单点压力过大,服务的提供者通常部署多台,如何从服务提供者集群中选取一个进行调用, 就依赖Dubbo的负载均衡策 ...

  5. Dubbo源码学习文章目录

    目录 Dubbo源码学习--服务是如何发布的 Dubbo源码学习--服务是如何引用的 Dubbo源码学习--注册中心分析 Dubbo源码学习--集群负载均衡算法的实现

  6. Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题

    Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题 相关文章: Dubbo源码学习文章目录 前言 主要是前一阵子换了工作,第一个任务就是解决目前团队在 Dubbo 停机时产生的问题 ...

  7. Dubbo源码学习(二)

    @Adaptive注解 在上一篇ExtensionLoader的博客中记录了,有两种扩展点,一种是普通的扩展实现,另一种就是自适应的扩展点,即@Adaptive注解的实现类. @Documented ...

  8. Dubbo源码(二) - SPI源码

    前情提要 假设你已经知道Dubbo SPI的使用方式,不知道的请出门左转: Dubbo源码(一) - SPI使用 Dubbo源码地址: apache/dubbo 本文使用版本:2.6.x 测试Demo ...

  9. Dubbo源码学习--服务发布(ServiceBean、ServiceConfig)

    前面讲过Dubbo SPI拓展机制,通过ExtensionLoader实现可插拔加载拓展,本节将接着分析Dubbo的服务发布过程. 以源码中dubbo-demo模块作为切入口一步步走进Dubbo源码. ...

  10. dubbo源码解析-spi(3)

    前言 在上一篇的末尾,我们提到了dubbo的spi中增加了IoC和AOP的功能.那么本篇就讲一下这个增加的IoC,spi部分预计会有四篇,因为这东西实在是太重要了.温故而知新,我们先来回顾一下,我们之 ...

随机推荐

  1. django登录注册验证之密码包含特殊字符,确认密码一致实现,Form验证

    Form验证的原理 首先用户在注册界面提交表单,后台收到表单之后通过request.post取到数据然后传入已经写好的Form类 执行obj.is_valid()这里的obj为Form的实例,在For ...

  2. Spring基础19——Spring中几种注解的区别

    1.@Autowired:注解是用来装配bean的,需要注入的bean必须是已经被IOC容器创建的bean,这个注解是利用类型装配的,如果容器中出现一个以上要装配的类或其子类就需要用@Qualifie ...

  3. 系统环境: CentOS 64位+千万不要在生产环境中升级glibc!

    # cd /lib64# LD_PRELOAD=/lib64/libc-2.15.so ln -sf /lib64/libc-2.15.so libc.so.6 libc-2.15.so 这个文件名根 ...

  4. 第二节,下载openwrt源码和编译环境

    文章的开始先说两个重点 1.不要使用root用户编译,普通用户编译即可. 2.自行搭建梯子,以免编译失败. 一,进入虚拟机内的Ubuntu系统 点击左下角的显示应用程序,我们去修改一下Ubuntu的源 ...

  5. blazeFace

    围绕四个点构造模型 1.扩大感受野 使用5*5卷积替换3*3来扩大感受野,在深度分离卷积中,pw与dw计算比为d/k^2,d为输出通道,k为 dw的卷积核,即增加dw的卷积核所带来的计算并不大. 在M ...

  6. 微信 PHP - SDK 包

    下载 个人公众号谢谢各位老铁支持

  7. 【vue-cli 3.0】 vue.config.js配置 - 路径别名

    如何配置vue-cli 3中vue.config.js的路径别名? 前段时间更新电脑重装了一下vue-cli,发现了vue-cli已经更新到3.0版.用来搭建项目后发现简化了很多,而且配置文件现在可以 ...

  8. hive group by distinct区别以及性能比较

    Hive去重统计 相信使用Hive的人平时会经常用到去重统计之类的吧,但是好像平时很少关注这个去重的性能问题,但是当一个表的数据量非常大的时候,会发现一个简单的count(distinct order ...

  9. Centos 6.4 x86_64 最小化安装后的优化——还需要整理

      Centos 6.4 x86_64 最小化安装后的优化   购买了服务器以后要做的第一件事就是安装操作系统了,这里推荐安装 Centos 6.4 x86_64,安装系统时要选择最小化安装(不需要图 ...

  10. Kohana重写接收不到get参数问题

    .htaccess,不需要重启apache # Turn on URL rewriting RewriteEngine On # Installation directory RewriteBase ...