前言
在Dubbo中有Filter使用,對於Filter來說我們會遇到這樣的問題,Filter自身有很多的實現,我們希望某種條件下使用A實現,另外情況下使用B實現,這個時候我們前面介紹@SPI和@Adaptive就不能滿足我們要求了,這個時候我們就需要使用@Activate。 Activate註解表示一個擴展是否被激活(使用),可以放在類定義和方法上,Dubbo中用它在擴展類定義上,表示這個擴展實現激活條件和時機。
如何使用
自定義接口;
@SPI
public interface ActivateDemo {
/**
* 測試
* @param msg
* @return
*/
String test(String msg);
}
實現接口,分別進行默認實現、多個組、排序、從URL獲取值,多種方式的案例;
@Activate(group = {"default"})
public class DefaultActivateDemoImpl implements ActivateDemo {
@Override
public String test(String msg) {
return msg;
}
}
@Activate(group = {"groupA","groupB"})
public class ComposeGroupActivateDemoImpl implements ActivateDemo {
@Override
public String test(String msg) {
return msg;
}
}
@Activate(order = 1, group = {"order"})
public class Order1ActivateDemoImpl implements ActivateDemo{
@Override
public String test(String msg) {
return msg;
}
}
@Activate(order = 2, group = {"order"})
public class Order2ActivateDemoImpl implements ActivateDemo{
@Override
public String test(String msg) {
return msg;
}
}
@Activate(value = {"value"}, group = {"group"})
public class ValueAndGroupActivateDemoImpl implements ActivateDemo{
@Override
public String test(String msg) {
return msg;
}
}
在resources下新建META-INF/dubbo/internal文件夾,新建自己定義接口的全限定名文件名,名稱以及內容可參考以下內容;
測試案例;
public static void main(String[] args) {
ExtensionLoader<ActivateDemo> loader = ExtensionLoader.getExtensionLoader(ActivateDemo.class);
URL url = URL.valueOf("test://localhost/test");
List<ActivateDemo> list = loader.getActivateExtension(url, new String[]{}, "order");
System.out.println(list.size());
list.forEach(item -> System.out.println(item.getClass()));
}
public static void main(String[] args) {
ExtensionLoader<ActivateDemo> loader = ExtensionLoader.getExtensionLoader(ActivateDemo.class);
URL url = URL.valueOf("test://localhost/test");
//注意這裏要使用url接收,不能直接url.addParameter()
url = url.addParameter("value", "test");
List<ActivateDemo> list = loader.getActivateExtension(url, new String[]{"order1", "default"}, "group");
System.out.println(list.size());
list.forEach(item -> System.out.println(item.getClass()));
}
源碼分析
@Activate註解標註在擴展實現類上,有 group、value 以及 order 三個屬性,三個屬性作用如下:
group 修飾的實現類可以列舉爲一種標籤,標籤用來區分是在 Provider 端被激活還是在 Consumer 端被激活; value 修飾的實現類只在 URL 參數中出現指定的 key 時纔會被激活; order 用來確定擴展實現類的排序;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
String[] group() default {};
String[] value() default {};
@Deprecated
String[] before() default {};
@Deprecated
String[] after() default {};
int order() default 0;
}
SPI在擴展類加載時候, loadClass() 方法會對 @Activate的註解類進行掃描,其中會將包含 @Activate 註解的實現類緩存到 cachedActivates 一個Map集合中,Key爲擴展名,Value爲@Activate註解;
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name,
boolean overridden) throws NoSuchMethodException {
if (!type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error occurred when loading extension class (interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + " is not subtype of interface.");
}
//判斷類是否加載Adaptive註解
if (clazz.isAnnotationPresent(Adaptive.class)) {
cacheAdaptiveClass(clazz, overridden);
//是否是擴展類,是的話就加入 cachedWrapperClasses 屬性
} else if (isWrapperClass(clazz)) {
cacheWrapperClass(clazz);
} else {
//檢測是否有默認構造起
clazz.getConstructor();
if (StringUtils.isEmpty(name)) {
//未配置擴展名,自動生成,主要用於兼容java SPI的配置。
name = findAnnotationName(clazz);
if (name.length() == 0) {
throw new IllegalStateException(
"No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
}
}
// 獲得擴展名,可以是數組,有多個拓擴展名。
String[] names = NAME_SEPARATOR.split(name);
if (ArrayUtils.isNotEmpty(names)) {
//如果是自動激活的實現類,則加入到緩存
cacheActivateClass(clazz, names[0]);
for (String n : names) {
//存儲Class到名字的映射關係
cacheName(clazz, n);
//存儲名字到Class的映射關係
saveInExtensionClass(extensionClasses, clazz, n, overridden);
}
}
}
}
使用cachedActivates這個集合的地方是 getActivateExtension() ,關於此方法有4個重載函數,核心方法包含三個重要參數,URL中包含了配置信息,Values是配置中指定的擴展名,Group標籤,下面是getActivateExtension的核心邏輯,首先就是獲取默認的擴展集合,其次將擴獲取到擴展類放到一個有序的集合中,按照順序添加自定義擴展類的實現。
public List<T> getActivateExtension(URL url, String key) {
return getActivateExtension(url, key, null);
}
public List<T> getActivateExtension(URL url, String[] values) {
return getActivateExtension(url, values, null);
}
public List<T> getActivateExtension(URL url, String key, String group) {
String value = url.getParameter(key);
return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group);
}
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> activateExtensions = new ArrayList<>();
// solve the bug of using @SPI's wrapper method to report a null pointer exception.
// TreeMap進行排序
TreeMap<Class, T> activateExtensionsMap = new TreeMap<>(ActivateComparator.COMPARATOR);
Set<String> loadedNames = new HashSet<>();
//傳入的數組包裝成爲set
Set<String> names = CollectionUtils.ofSet(values);
//包裝好的數據中判斷不含"-default""
if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
//獲取所有的加載類型
getExtensionClasses();
//cachedActivate 存儲被@Activate修飾類型
for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
String name = entry.getKey();
Object activate = entry.getValue();
String[] activateGroup, activateValue;
//兼容老的邏輯
if (activate instanceof Activate) {
activateGroup = ((Activate) activate).group();
activateValue = ((Activate) activate).value();
} else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) {
activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group();
activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value();
} else {
continue;
}
//判斷group是否匹配
if (isMatchGroup(group, activateGroup)
//沒有出現在values配置中的,即爲默認激活的擴展實現
&& !names.contains(name)
//通過"-"明確指定不激活該擴展實現
&& !names.contains(REMOVE_VALUE_PREFIX + name)
//檢測URL中是否出現了指定的Key
&& isActive(activateValue, url)
//去重判斷
&& !loadedNames.contains(name)) {
//篩入treeMap中
activateExtensionsMap.put(getExtensionClass(name), getExtension(name));
loadedNames.add(name);
}
}
if (!activateExtensionsMap.isEmpty()) {
activateExtensions.addAll(activateExtensionsMap.values());
}
}
List<T> loadedExtensions = new ArrayList<>();
for (String name : names) {
//排除對應擴展名 不包含以-開始 以及 一+name
if (!name.startsWith(REMOVE_VALUE_PREFIX)
&& !names.contains(REMOVE_VALUE_PREFIX + name)) {
if (!loadedNames.contains(name)) {
if (DEFAULT_KEY.equals(name)) {
if (!loadedExtensions.isEmpty()) {
activateExtensions.addAll(0, loadedExtensions);
loadedExtensions.clear();
}
} else {
//獲取對應名字擴展
loadedExtensions.add(getExtension(name));
}
loadedNames.add(name);
} else {
// If getExtension(name) exists, getExtensionClass(name) must exist, so there is no null pointer processing here.
String simpleName = getExtensionClass(name).getSimpleName();
logger.warn("Catch duplicated filter, ExtensionLoader will ignore one of them. Please check. Filter Name: " + name +
". Ignored Class Name: " + simpleName);
}
}
}
if (!loadedExtensions.isEmpty()) {
activateExtensions.addAll(loadedExtensions);
}
return activateExtensions;
}
結束
歡迎大家點點關注,點點贊!