前言
Active注解可以被框架中自动激活加载扩展,此Annotation用于配置扩展被自动激活加载条件,不过有时机上的选择。
理解:比如dubbo的Filter
拦截器,我们有很多的实现存在。但是有些是必须要立刻加载的,访问服务日志拦截器AccessLogFilter
如下:
@Activate(group = PROVIDER, value = ACCESS_LOG_KEY)
public class AccessLogFilter implements Filter
当提供者的URL中含有accesslog才加载,这里的控制就是需要Active
注解。首先,Dubbo加载这些文件,其次,单需要这个Filter时,使用ExtensionLoader
的方法getActivateExtension(URL url, String key)
来加载对应的实现,就能按条件激活了。
Active源码
/**
* 这个注解能够自动激活某些的类
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
//激活Group的过滤条件
String[] group() default {};
//Keys过滤条件
String[] value() default {};
//加载这些扩展的排序
int order() default 0;
}
getExtension
ExtensionLoader 中含有ClassCache,用于加载标注Actives的Class。
//cache actives类
private final Map<String, Object> cachedActivates = new ConcurrentHashMap();
//加载所有标注SPI的class,这个方法在加载所有的SPI实现时,里面有个chachedActivates的缓存代码。
private Map<String, Class<?>> getExtensionClasses()
//加载class时,拿到加载文件的name
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
...
String[] names = NAME_SEPARATOR.split(name);
if (ArrayUtils.isNotEmpty(names)) {
cacheActivateClass(clazz, names[0]);
for (String n : names) {
cacheName(clazz, n);
saveInExtensionClass(extensionClasses, clazz, n);
}
}
}
//找到name对应的class之后,看到是否含有类的Actives注解
private void cacheActivateClass(Class<?> clazz, String name) {
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(name, activate);
} else {
// support com.alibaba.dubbo.common.extension.Activate
com.alibaba.dubbo.common.extension.Activate oldActivate = clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.class);
if (oldActivate != null) {
cachedActivates.put(name, oldActivate);
}
}
}
getActiveExtension(…)
这里所有的方法最后调用一个调用加载
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);
}
getActivateExtension
真正的加载
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> activateExtensions = new ArrayList<>();
List<String> names = values == null ? new ArrayList<>(0) : Arrays.asList(values);
if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
//加载SPI标记的所有的class,这时候含有Actives的类就会缓存下来
getExtensionClasses();
for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
//spi 扩展名
String name = entry.getKey();
//Actives注解对应的values
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和标注的activeGroup是否一致
if (isMatchGroup(group, activateGroup)
&& !names.contains(name)
&& !names.contains(REMOVE_VALUE_PREFIX + name)
&& isActive(activateValue, url)) {
activateExtensions.add(getExtension(name));
}
}
//排序Activate 具体实现在ActivateComparator里,实现了Comparator 接口compare方法
activateExtensions.sort(ActivateComparator.COMPARATOR);
}
List<T> loadedExtensions = new ArrayList<>();
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
if (!name.startsWith(REMOVE_VALUE_PREFIX)
&& !names.contains(REMOVE_VALUE_PREFIX + name)) {
//遍历所有没有排除的扩展名
if (DEFAULT_KEY.equals(name)) {
if (!loadedExtensions.isEmpty()) {
activateExtensions.addAll(0, loadedExtensions);
loadedExtensions.clear();
}
} else {
//通过扩展名,加载扩展添加到结果集
loadedExtensions.add(getExtension(name));
}
}
}
if (!loadedExtensions.isEmpty()) {
activateExtensions.addAll(loadedExtensions);
}
return activateExtensions;
}