在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;
}
结束
欢迎大家点点关注,点点赞!