在Dubbo中有Filter使用,對於Filter來說我們會遇到這樣的問題,Filter自身有很多的實現,我們希望某種條件下使用A實現,另外情況下使用B實現,這個時候我們前面介紹@SPI和@Adaptive就不能滿足我們要求了,這個時候我們就需要使用@Activate。 Activate註解表示一個擴充套件是否被啟用(使用),可以放在類定義和方法上,Dubbo中用它在擴充套件類定義上,表示這個擴充套件實現啟用條件和時機。
@SPI
public interface ActivateDemo {
/**
* 測試
* @param msg
* @return
*/
String test(String msg);
}
@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;
}
}
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 三個屬性,三個屬性作用如下:
@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;
}
歡迎大家點點關注,點點贊!