spi(service providerinterface),是DUBBO功能强大的保障。核心支持类ExtensionLoader。

具体分析可以参照<Dubbo原理解析-Dubbo内核实现之基于SPI思想Dubbo内核实现>.

1.比较重要的注解

@SPI:扩展点接口的标识 :作用域在类上;

@Adaptive:为生成Adaptive实例提供参数,作用域在类或方法上;

Adatpive,字面意思是个适配,但其实是个代理,它的意思是适配合适的对象处理请求。类似jdk的动态代理,因为dubbo底层会大量使用反射,出于性能考虑会默认使用javassist字节码编译生成一个adaptive拦截所有请求,然后由它基于策略动态委派合适的provider进行处理。
SPI接口会动态编译出一个adaptive,用于适配provider处理请求。用户可以自己实现一个adaptive,只需要对某个provider打上@adaptive即可,例如Dubbo自身的AdaptiveExtensionFactory类。
对于默认编译生成Adaptive的方案,需要使用@Adaptive声明接口上的哪些方法是adaptive方法,没有被声明的方法如果被请求会抛出异常非adaptive方法的异常

@Activate:可以被框架中自动激活加载扩展,此Annotation用于配置扩展被自动激活加载条件。

Activate,看起来有点不好理解,它的意思是条件激活,用户通过group和value配置激活条件。被activate注解的扩展点在满足某种条件时会被激活,它一般用来配合filter和Invokelistener,声明他们的使用场景。

1.1 测试对象代码

#1.声明SPI 默认为imp1
@SPI("impl1")
public interface SimpleExt {// 没有使用key的@Adaptive !@AdaptiveString echo(URL url, String s);@Adaptive({"key1", "key2"})String yell(URL url, String s);// 无@Adaptive !String bang(URL url, int i);
}//实现类1
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";}
}
//实现类2
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";}}
//实现类3
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";}}

1.2 配置文件com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt

位置要放在如下位置

private static final String SERVICES_DIRECTORY = "META-INF/services/";private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";

具体内容如下

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

定义3个实现。

1.4 测试

@Test
public void test_getDefaultExtension() throws Exception {SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getDefaultExtension();assertThat(ext, instanceOf(SimpleExtImpl1.class));String name = ExtensionLoader.getExtensionLoader(SimpleExt.class).getDefaultExtensionName();assertEquals("impl1", name);
}

由于@SPI("impl1"),定义了默认实现的名称为imp1.

@Test
public void test_getExtension() throws Exception {assertTrue(ExtensionLoader.getExtensionLoader(SimpleExt.class).getExtension("impl1") instanceof SimpleExtImpl1);assertTrue(ExtensionLoader.getExtensionLoader(SimpleExt.class).getExtension("impl2") instanceof SimpleExtImpl2);
}

getExtensionLoader(Class<T> type):根据类名,返回具体实现类。这些配置信息在META对应文件中配置。当然,也可以使用@Extention注解配置(只不过,这个注解已经废弃了)

@Test
public void test_getAdaptiveExtension_defaultAdaptiveKey() throws Exception {{SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getAdaptiveExtension();Map<String, String> map = new HashMap<String, String>();//没有指定具体parameters参数,所以选用默认实现,最后返回impl1URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);//如果不设置默认的SPI实现类,则报异常//java.lang.IllegalStateException: Fail to get extension(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt) name from url(p1://1.2.3.4:1010/path1) use keys([simple.ext])String echo = ext.echo(url, "haha");assertEquals("Ext1Impl1-echo", echo);}{SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getAdaptiveExtension();Map<String, String> map = new HashMap<String, String>();map.put("simple.ext", "impl2");//手动在参数中配置impl2,参数为simple.extURL url = new URL("p1", "1.2.3.4", 1010, "path1", map);String echo = ext.echo(url, "haha");assertEquals("Ext1Impl2-echo", echo);}
}

@Adaptive 测试

由于 yell方法声明了,@Adaptive({"key1", "key2"})

@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);String echo = ext.yell(url, "haha");assertEquals("Ext1Impl2-yell", echo);url = url.addParameter("key1", "impl3"); // 注意: URL是值类型echo = ext.yell(url, "haha");assertEquals("Ext1Impl3-yell", echo);
}

如果参数不是key1,key2,即使参数值输入impl1,impl2也是无意义的。

由于bang方法,没有被@Adaptive 修饰,所以以下代码,会报异常

ExtensionLoader.getExtensionLoader(SimpleExt.class).getAdaptiveExtension().bang(..);

of interface com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt is not adaptive method!

2.@Activate注解

配置文件

group=com.alibaba.dubbo.common.extensionloader.activate.impl.GroupActivateExtImpl
value=com.alibaba.dubbo.common.extensionloader.activate.impl.ValueActivateExtImpl
order1=com.alibaba.dubbo.common.extensionloader.activate.impl.OrderActivateExtImpl1
order2=com.alibaba.dubbo.common.extensionloader.activate.impl.OrderActivateExtImpl2
com.alibaba.dubbo.common.extensionloader.activate.impl.ActivateExt1Impl1
@SPI("impl1")
public interface ActivateExt1 {String echo(String msg);
}
@Activate(group = {"default_group"})
public class ActivateExt1Impl1 implements ActivateExt1 {public String echo(String msg) {return msg;}
}
@Activate(group = {"group1", "group2"})
public class GroupActivateExtImpl implements ActivateExt1 {public String echo(String msg) {return msg;}
}
@Activate(order = 1, group = {"order"})
public class OrderActivateExtImpl1 implements ActivateExt1 {public String echo(String msg) {return msg;}
}
@Activate(order = 2, group = {"order"})
public class OrderActivateExtImpl2 implements ActivateExt1 {public String echo(String msg) {return msg;}
}
@Activate(value = {"value"}, group = {"value"})
public class ValueActivateExtImpl implements ActivateExt1 {public String echo(String msg) {return msg;}
}

2.2测试代码

   @Testpublic void testLoadActivateExtension() throws Exception {// test defaultURL url = URL.valueOf("test://localhost/test");List<ActivateExt1> list = ExtensionLoader.getExtensionLoader(ActivateExt1.class).getActivateExtension(url, new String[]{}, "default_group");Assert.assertEquals(1, list.size());Assert.assertTrue(list.get(0).getClass() == ActivateExt1Impl1.class);// test group
//        url = url.addParameter(Constants.GROUP_KEY, "group1");list = ExtensionLoader.getExtensionLoader(ActivateExt1.class).getActivateExtension(url, new String[]{}, "group1");Assert.assertEquals(1, list.size());Assert.assertTrue(list.get(0).getClass() == GroupActivateExtImpl.class);// test valueurl = url.removeParameter(Constants.GROUP_KEY);
//        url = url.addParameter(Constants.GROUP_KEY, "value");url = url.addParameter("value", "value");list = ExtensionLoader.getExtensionLoader(ActivateExt1.class).getActivateExtension(url, new String[]{}, "value");Assert.assertEquals(1, list.size());Assert.assertTrue(list.get(0).getClass() == ValueActivateExtImpl.class);// test orderurl = URL.valueOf("test://localhost/test");
//        url = url.addParameter(Constants.GROUP_KEY, "order");list = ExtensionLoader.getExtensionLoader(ActivateExt1.class).getActivateExtension(url, new String[]{}, "order");Assert.assertEquals(2, list.size());Assert.assertTrue(list.get(0).getClass() == OrderActivateExtImpl1.class);Assert.assertTrue(list.get(1).getClass() == OrderActivateExtImpl2.class);}

以上内容,是通过代码演练的方式,讲解了dubbo SPI机制的威力。
如果对底层实现感兴趣,可参看博客。

参照:Dubbo原理解析-Dubbo内核实现之基于SPI思想Dubbo内核实现

转载于:https://blog.51cto.com/dba10g/1880962

Dubbo点滴(1) SPI入门相关推荐

  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. Maven+SpringMVC+Dubbo+zookeeper 简单的入门demo配置

    参考:http://blog.csdn.net/aixiaoyang168/article/details/51362675 dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调 ...

  4. Spring boot 之 dubbo 无xml 简单入门

    Dubbo简介 Dubbo框架设计一共划分了10个层,而最上面的Service层是留给实际想要使用Dubbo开发分布式服务的开发者实现业务逻辑的接口层.图中左边淡蓝背景的为服务消费方使用的接口,右边淡 ...

  5. Dubbo分布式服务框架入门(附工程)

    要想了解Dubbo是什么,我们不防先了解它有什么用. 使用场景:比如我想开发一个网上商城项目,这个网上商城呢,比较复杂,分为pc端web管理后台,微信端销售公众号,那么我们分成四个项目,pc端网站,微 ...

  6. JDK/Dubbo/Spring 三种 SPI 机制,谁更好?

    点击关注公众号,Java干货及时送达 来源:juejin.cn/post/6950266942875779108 SPI 全称为 Service Provider Interface,是一种服务发现机 ...

  7. Dubbo 源码分析 - SPI 机制

    1.简介 SPI 全称为 Service Provider Interface,是一种服务发现机制.SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类.这样可以 ...

  8. JDK/Dubbo/Spring 三种 SPI 机制,谁更好呢?

    JDK/Dubbo/Spring 三种 SPI 机制,谁更好? SPI 全称为 Service Provider Interface,是一种服务发现机制.SPI 的本质是将接口实现类的全限定名配置在文 ...

  9. JDK、Spring、Dubbo SPI 原理介绍

    导读: 需求变化是程序员生命中唯一不变的事情,本文将介绍 JDK/Spring/Dubbo 中的 SPI 机制,以此来帮助我们编写出一套可扩展性强,易于维护的代码框架. 文|杨亮 网易云商高级 Jav ...

最新文章

  1. android+单利模式中传递context,Android的Context详解
  2. MYSQL远程连接数据库
  3. python平均工资-2019年我国程序员薪资统计,看看你出于什么水平?
  4. Android中再按一次退出实现
  5. Break,Continue,Return 傻傻分不清楚
  6. ubuntu java 全屏显示_java 在ubuntu下实现全屏,上面的状态栏依然显示。如下图,不想要上面的状态栏...
  7. 我的Android进阶之旅------Android MediaPlayer播放网络音频的实例--网络mp3播放器
  8. python教程七牛云_python-django框架中使用七牛云
  9. Vista忘记密码如何登录?
  10. Quartz教程五:SimpleTrigger
  11. git branch -M main时报错
  12. 基于SSH房地产销售系统
  13. 全志平台BSP裁剪(2)附件一 General setup配置说明
  14. 信息系统项目管理师(高项)考试的论文怎么写,怎么背?经验分享
  15. graphlan/iTOL画进化树记录
  16. 挖金矿问题(c++求解)
  17. 教你文本聚类(参考http://www.kuqin.com/searchengine/20080511/8323.html)
  18. 微信小程序作品集实例:跨页面传参,数据库,换行,空格,css
  19. 学java被“劝退”的第七天
  20. 这9个程序员岗位最牛!AI百万年薪夺冠

热门文章

  1. 【Webview相关问题】登陆失败之cookie陷阱
  2. truffle详细使用教程
  3. 【UIKit】UIAlertController使用
  4. Mac 配置flutter
  5. Java锁优化思路及JVM实现
  6. URL参数解析与反解析
  7. 《Lua程序设计》第6章 深入函数 学习笔记
  8. 【实习项目记录】(一)加密算法MD5和RSA
  9. 使用Xpand XAF创建项目产生Quartz错误的解决方法
  10. 今天准备正式开博了!专注于Silverlight!