博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Dubbo点滴(1) SPI入门
阅读量:6954 次
发布时间:2019-06-27

本文共 8248 字,大约阅读时间需要 27 分钟。

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注解

配置文件

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,如需转载请自行联系原作者

你可能感兴趣的文章
虚拟化这八年-【软件和信息服务】2014.11
查看>>
使用swfupload上传超过30M文件,使用FLASH上传组件
查看>>
OkHttp简介
查看>>
如何使用通用Mapper
查看>>
MYSQL建表语法(主键,外键,联合主键)
查看>>
多线程的通信和同步(Java并发编程的艺术--笔记)
查看>>
Linux使用du和df查看磁盘和文件夹占用空间
查看>>
CentOS 6.6 MySQL install
查看>>
从零开始用gulp
查看>>
android之Activity的生命周期
查看>>
hadoop2.4 支持snappy
查看>>
STL 笔记(四) 迭代器 iterator
查看>>
2017"百度之星"程序设计大赛 - 复赛1003&&HDU 6146 Pokémon GO【数学,递推,dp】
查看>>
[LeetCode] Valid Parenthesis String 验证括号字符串
查看>>
各大SRC中的CSRF技巧
查看>>
Docker for Windows 使用入门
查看>>
【Django】Web应用开发经由
查看>>
SpringBoot(九)-- SpringBoot JDBC
查看>>
Spring + Mybatis - 原始dao开发整合 与 Mapper代理整合
查看>>
基于Centos搭建nginx+uwsgi运行django环境
查看>>