Dubbo點滴(1) SPI入門

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

wKiom1hOnOTwfwoCAABY6AQUXtA046.png

配置文件

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機制的威力。
若是對底層實現感興趣,可參看博客。

參照:Dubbo原理解析-Dubbo內核實現之基於SPI思想Dubbo內核實現

相關文章
相關標籤/搜索