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