Dubbo点滴(1) SPI入门

简介:

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# 1 .声明SPI 默认为imp1
@SPI ( "impl1" )
public  interface  SimpleExt {
     // 没有使用key的@Adaptive !
     @Adaptive
     String 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

位置要放在如下位置

1
2
3
4
5
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/" ;

具体内容如下

1
2
3
4
# Comment 1
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

定义3个实现。

1.4 测试

1
2
3
4
5
6
7
8
@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.

1
2
3
4
5
@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注解配置(只不过,这个注解已经废弃了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Test
public  void  test_getAdaptiveExtension_defaultAdaptiveKey()  throws  Exception {
     {
         SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt. class ).getAdaptiveExtension();
 
         Map<String, String> map =  new  HashMap<String, String>();
         //没有指定具体parameters参数,所以选用默认实现,最后返回impl1
         URL 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.ext
         URL 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"})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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注解

wKiom1hOnOTwfwoCAABY6AQUXtA046.png

配置文件

1
2
3
4
5
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@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测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
    @Test
     public  void  testLoadActivateExtension()  throws  Exception {
         // test default
         URL 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 value
         url = 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 order
         url = 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机制的威力。
如果对底层实现感兴趣,可参看博客。




本文转自 randy_shandong 51CTO博客,原文链接:http://blog.51cto.com/dba10g/1880962,如需转载请自行联系原作者

相关文章
|
存储 负载均衡 Dubbo
深入理解Dubbo-4.Dubbo扩展SPI
深入理解Dubbo-4.Dubbo扩展SPI
439 1
|
缓存 Dubbo Java
趁同事上厕所的时间,看完了 Dubbo SPI 的源码,瞬间觉得 JDK SPI 不香了
趁同事上厕所的时间,看完了 Dubbo SPI 的源码,瞬间觉得 JDK SPI 不香了
|
监控 Dubbo Java
由浅入深Dubbo核心源码剖析SPI机制 2
由浅入深Dubbo核心源码剖析SPI机制
373 0
|
缓存 Dubbo Java
由浅入深Dubbo核心源码剖析SPI机制 1
由浅入深Dubbo核心源码剖析SPI机制
336 0
|
缓存 Dubbo Java
Dubbo 第三节_ Dubbo的可扩展机制SPI源码解析
Dubbo会对DubboProtocol对象进⾏依赖注⼊(也就是⾃动给属性赋值,属性的类型为⼀个接⼝,记为A接⼝),这个时候,对于Dubbo来说它并不知道该给这个属性赋什么值,换句话说,Dubbo并不知道在进⾏依赖注⼊时该找⼀个什么的的扩展点对象给这个属性,这时就会预先赋值⼀个A接⼝的⾃适应扩展点实例,也就是A接⼝的⼀个代理对象。在调⽤getExtension去获取⼀个扩展点实例后,会对实例进⾏缓存,下次再获取同样名字的扩展点实例时就会从缓存中拿了。Protocol是⼀个接。但是,不是只要在⽅法上加了。
365 0
|
SpringCloudAlibaba Dubbo Java
【SpringCloud Alibaba系列】Dubbo基础入门篇
Dubbo是一款高性能、轻量级的开源Java RPC框架,提供面向接口代理的高性能RPC调用、智能负载均衡、服务自动注册和发现、运行期流量调度、可视化服务治理和运维等功能。
1068 1
【SpringCloud Alibaba系列】Dubbo基础入门篇
|
Dubbo Java 应用服务中间件
Dubbo学习圣经:从入门到精通 Dubbo3.0 + SpringCloud Alibaba 微服务基础框架
尼恩团队的15大技术圣经,旨在帮助开发者系统化、体系化地掌握核心技术,提升技术实力,从而在面试和工作中脱颖而出。本文介绍了如何使用Dubbo3.0与Spring Cloud Gateway进行整合,解决传统Dubbo架构缺乏HTTP入口的问题,实现高性能的微服务网关。
|
Dubbo Java 应用服务中间件
分布式-dubbo的入门
分布式-dubbo的入门
|
负载均衡 Dubbo Java
Dubbo服务Spi机制和原理
该文章主要介绍了Dubbo中的SPI(Service Provider Interface)机制和原理,包括SPI的基本概念、Dubbo中的SPI分类以及SPI机制的实现细节。
Dubbo服务Spi机制和原理
|
监控 负载均衡 Dubbo
分布式架构与Dubbo基础入门与实践
分布式架构与Dubbo基础入门与实践
398 1