6、Dubbo的SPI機制分析5-Activate詳解
阿新 • • 發佈:2019-10-07
group=com.alibaba.dubbo.demo.provider.activate.impl.GroupActivateExtImpl key=com.alibaba.dubbo.demo.provider.activate.impl.ValueActivateExtImpl order1=com.alibaba.dubbo.demo.provider.activate.impl.OrderActivateExtImpl1 order2=com.alibaba.dubbo.demo.provider.activate.impl.OrderActivateExtImpl2 com.alibaba.dubbo.demo.provider.activate.impl.ActivateExtImpl1
下面是幾個單元測試及其測試結果,觀察測試結果我們可以得出以下結論:
- 如果
loader.getActivateExtension(URL url, String[] values, String group)
中的group引數能夠匹配到,就是我們要選擇的,test1中根據group=default匹配到了ActivateExtImpl1,它也是在此條件下需要啟用的 - @Activate中的value是第二層校驗引數,第一層是group,在group校驗通過的情況下,
如果URL中的key引數與類@Activate註解中的value引數一致且key引數對應的值不為空
,則才會被選中 - order越小,優先順序越高
/** * 1 * class com.alibaba.dubbo.demo.provider.activate.impl.ActivateExtImpl1 */ @Test public void test1(){ ExtensionLoader<ActivateExt> loader = ExtensionLoader.getExtensionLoader(ActivateExt.class); URL url = URL.valueOf("test://localhost/test"); List<ActivateExt> list = loader.getActivateExtension(url, new String[]{}, "default"); System.out.println(list.size()); list.forEach(item -> System.out.println(item.getClass())); }
/**
* 1
* class com.alibaba.dubbo.demo.provider.activate.impl.GroupActivateExtImpl
*/
@Test
public void test2(){
ExtensionLoader<ActivateExt> loader = ExtensionLoader.getExtensionLoader(ActivateExt.class);
URL url = URL.valueOf("test://localhost/test");
List<ActivateExt> list = loader.getActivateExtension(url, new String[]{}, "group1");
System.out.println(list.size());
list.forEach(item -> System.out.println(item.getClass()));
}
/**
* 2
* class com.alibaba.dubbo.demo.provider.activate.impl.OrderActivateExtImpl1
* class com.alibaba.dubbo.demo.provider.activate.impl.ValueActivateExtImpl
*/
@Test
public void test3(){
ExtensionLoader<ActivateExt> loader = ExtensionLoader.getExtensionLoader(ActivateExt.class);
URL url = URL.valueOf("test://localhost/test");
// 注意這裡要使用url接收,不能直接url.addParameter()
url = url.addParameter("value", "test");
List<ActivateExt> list = loader.getActivateExtension(url, new String[]{"order1", "default"}, "group");
System.out.println(list.size());
list.forEach(item -> System.out.println(item.getClass()));
}
/**
* 2
* class com.alibaba.dubbo.demo.provider.activate.impl.OrderActivateExtImpl2
* class com.alibaba.dubbo.demo.provider.activate.impl.OrderActivateExtImpl1
*/
@Test
public void test4(){
ExtensionLoader<ActivateExt> loader = ExtensionLoader.getExtensionLoader(ActivateExt.class);
URL url = URL.valueOf("test://localhost/test");
List<ActivateExt> list = loader.getActivateExtension(url, new String[]{}, "order");
System.out.println(list.size());
list.forEach(item -> System.out.println(item.getClass()));
}
2、原始碼分析
public void test3(){
ExtensionLoader<ActivateExt> loader = ExtensionLoader.getExtensionLoader(ActivateExt.class);
URL url = URL.valueOf("test://localhost/test");
// 注意這裡要使用url接收,不能直接url.addParameter()
url = url.addParameter("value", "test");
List<ActivateExt> list = loader.getActivateExtension(url, new String[]{"order1", "default"}, "group");
System.out.println(list.size());
list.forEach(item -> System.out.println(item.getClass()));
}
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> exts = new ArrayList<T>();
// 將傳遞進來的values包裝成List型別的names
List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
// 包裝好的資料中不含"-default"
if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
// 獲取這個型別的資料的所有擴充套件資訊
getExtensionClasses();
for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
// 獲取拓展的名稱,在配置檔案中配置的key=value中的key值
String name = entry.getKey();
// 獲取註解
Activate activate = entry.getValue();
// 判斷group是否匹配,group是程式傳進來的值,如果沒有設定則返回true表示匹配,
// 如果設定了需要進行比較匹配
if (isMatchGroup(group, activate.group())) {
// 獲取key值即name對應的例項
T ext = getExtension(name);
if (!names.contains(name)
&& !names.contains(Constants.REMOVE_VALUE_PREFIX + name)
&& isActive(activate, url)) {
exts.add(ext);
}
}
}
// 排序
Collections.sort(exts, ActivateComparator.COMPARATOR);
}
List<T> usrs = new ArrayList<T>();
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
if (!name.startsWith(Constants.REMOVE_VALUE_PREFIX)
&& !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {
if (Constants.DEFAULT_KEY.equals(name)) {
// 如果name是default,就將usrs加入exts頭部並清空usrs
if (!usrs.isEmpty()) {
exts.addAll(0, usrs);
usrs.clear();
}
} else {
// 獲取name對應的拓展並加入usrs
T ext = getExtension(name);
usrs.add(ext);
}
}
}
if (!usrs.isEmpty()) {
exts.addAll(usrs);
}
return exts;
}
上述程式碼中用到的cachedActivates
是在getExtensionClasses()
方法中賦值的,具體可以參考:
Dubbo的SPI機制分析1-SPI載入class
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(names[0], activate);
}
// 判斷group是否匹配,很好理解
private boolean isMatchGroup(String group, String[] groups) {
if (group == null || group.length() == 0) {
return true;
}
if (groups != null && groups.length > 0) {
for (String g : groups) {
if (group.equals(g)) {
return true;
}
}
}
return false;
}
// 將url中的鍵key與Activate註解中的value值做匹配,並且key所對應的value值不能為空
private boolean isActive(Activate activate, URL url) {
String[] keys = activate.value();
if (keys.length == 0) {
return true;
}
for (String key : keys) {
for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {
String k = entry.getKey();
String v = entry.getValue();
if ((k.equals(key) || k.endsWith("." + key))
&& ConfigUtils.isNotEmpty(v)) {
return true;
}
}
}
return false;
}
注意,如果是以下這種形式,傳入的values中有-default,則所有的匹配可以通過的@Activate都不會被啟用,只有values中指定的可以被啟用。如果傳入了“-”符合開頭的擴充套件類名,則該擴充套件點也不會被啟用。
// 輸出class com.alibaba.dubbo.demo.provider.activate.impl.OrderActivateExtImpl1
ExtensionLoader<ActivateExt> loader = ExtensionLoader.getExtensionLoader(ActivateExt.class);
URL url = URL.valueOf("test://localhost/test");
// 注意這裡要使用url接收,不能直接url.addParameter()
url = url.addParameter("value", "test");
List<ActivateExt> list = loader.getActivateExtension(url, new String[]{"order1", "-default"}, "group");
System.out.println(list.size());
list.forEach(item -> System.out.println(item.ge