spi(service providerinterface),是DUBBO功能强大的保障。核心支持类ExtensionLoader。html
具体分析能够参照<Dubbo原理解析-Dubbo内核实现之基于SPI思想Dubbo内核实现>.java
1.比较重要的注解
框架
@SPI:扩展点接口的标识 :做用域在类上;ide
@Adaptive:为生成Adaptive实例提供参数,做用域在类或方法上;性能
Adatpive,字面意思是个适配,但实际上是个代理,它的意思是适配合适的对象处理请求。相似jdk的动态代理,由于dubbo底层会大量使用反射,出于性能考虑会默认使用javassist字节码编译生成一个adaptive拦截全部请求,而后由它基于策略动态委派合适的provider进行处理。
SPI接口会动态编译出一个adaptive,用于适配provider处理请求。用户能够本身实现一个adaptive,只须要对某个provider打上@adaptive便可,例如Dubbo自身的AdaptiveExtensionFactory类。
对于默认编译生成Adaptive的方案,须要使用@Adaptive声明接口上的哪些方法是adaptive方法,没有被声明的方法若是被请求会抛出异常非adaptive方法的异常测试
@Activate:能够被框架中自动激活加载扩展,此Annotation用于配置扩展被自动激活加载条件。url
Activate,看起来有点很差理解,它的意思是条件激活,用户经过group和value配置激活条件。被activate注解的扩展点在知足某种条件时会被激活,它通常用来配合filter和Invokelistener,声明他们的使用场景。
spa
1.1 测试对象代码
.net
#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代理
位置要放在以下位置
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 2 impl3=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参数,因此选用默认实现,最后返回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"})
@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测试代码
@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机制的威力。
若是对底层实现感兴趣,可参看博客。