注:本文基于dubbo v2.6.x
1.具体实现
我们在介绍注解的时候,曾介绍过@Activate注解,这个注解就是标识自动激活的,主要是用在有多个扩展点实现,然后根据不同条件被激活的场景中,比如说Filter需要多个同时激活,因为每个Filter实现的是不同的功能。
讲解自动激活实现,需要举个例子,那就是我们dubbo框架中的Filter,ProtocolFilterWrapper 是Protocol扩展点实现类的包装类,在我们服务一启动 暴露服务的时候,我们会获取Protocol扩展点的实现类,我们得到的实现类是经过ExtensionLoader为我们包装过的实现类,其中就被ProtocolFilterWrapper包装过,当我们暴露服务的时候就会先执行ProtocolFilterWrapper中export方法,我们来看下代码
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {/// registry protocol 就到下一个wapper 包装对象中就可以了
return protocol.export(invoker);
}
return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
}
首先判断这个Protocol是不是registry的,如果是的话就直接跳过的了,也就是没它什么事了。
如果不是就要走下面这个,这里调用了 buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER)方法,其中 Constants.SERVICE_FILTER_KEY是service.filter,Constants.PROVIDER 是provider,我们来看下这个方法。
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
Invoker<T> last = invoker; // size = 8
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
if (!filters.isEmpty()) {
for (int i = filters.size() - 1; i >= 0; i--) {
final Filter filter = filters.get(i);
final Invoker<T> next = last;
last = new Invoker<T>() {
@Override
public Class<T> getInterface() {
return invoker.getInterface();
}
@Override
public URL getUrl() {
return invoker.getUrl();
}
@Override
public boolean isAvailable() {
return invoker.isAvailable();
}
//exception->moniter->timeout->trace->context->generic->classloader->echo
@Override
public Result invoke(Invocation invocation) throws RpcException {
return filter.invoke(next, invocation);
}
@Override
public void destroy() {
invoker.destroy();
}
@Override
public String toString() {
return invoker.toString();
}
};
}
}
return last;
}
这个方法执行了List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
,这里我们本文的主角就登场了,获取了Filter的ExtensionLoader对象,然后调用getActivateExtension方法,参数分别是url,service.filter,provider,接着我们要看下这个方法了
public List<T> getActivateExtension(URL url, String key, String group) {
String value = url.getParameter(key);
return getActivateExtension(url, value == null || value.length() == 0 ? null : Constants.COMMA_SPLIT_PATTERN.split(value), group);
}
根据service.filter 这个key从url中获取value,然后调用重载方法
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> exts = new ArrayList<T>();
List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
getExtensionClasses();
for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
String name = entry.getKey();
Activate activate = entry.getValue();
if (isMatchGroup(group, activate.group())) {
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) // name没有-开头或者是names中没有-xxx 这种
&& !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {
if (Constants.DEFAULT_KEY.equals(name)) {
if (!usrs.isEmpty()) {
exts.addAll(0, usrs);
usrs.clear();
}
} else {
T ext = getExtension(name);
usrs.add(ext);
}
}
}
if (!usrs.isEmpty()) {
exts.addAll(usrs);
}
return exts;
}
首先将values放到names这个list,然后判断如果names这个list中没有-default的话就继续执行,这个-default是用户自己配置的,表示不使用这些filter。
接着就是调用getExtensionClasses(), 这个方法之前咱们说过,就是加载扩展点的所有实现类,这个方法执行完后,该扩展点的实现类会被分类缓存。
接着就是遍历cachedActivates 这个map了,这个map存的是扩展点实现类上的@Activate注解,key是name,值扩展点实现类上的@Activate注解。
接着就是获取注解上group属性,判断如果是这个group的,就继续根据name获取扩展点的实现类,接着再判断names里面没有包含这个name,然后也没有-name,之后就是activate的value 在url有对应参数,同时满足这个三个条件,然后会将这个扩展点实现类添加到开始定义好的exts中。
接着就是对exts排序,排序规则在ActivateComparator这个类中实现了Comparator 接口compare方法
接着就是再遍历一下names,如果name没有-开头或者是names中没有-xxx ,然后就通过name获取到对应的扩展点实现,添加到usrs这个list中,最后判断usrs这个list不是空的话,就将usrs元素扔到exts中,然后return exts。
2.总结
@Activate有多个属性,我们再来回顾一下
- String[] group ,这个属性是分组的,在我们服务提供者就是provider,然后服务消费者那边就是consumer,你没配置组也就算了,如果你配置了组,就会把不是你这个组的给过滤掉。
- String[] value ,这个参数也是个数组形式,他会查找url中如果包含该key值就会,就会激活。咱们上面代码中也看到了判断中有个isActive(activate, url) ,其实这个方法就是把activate的value值拿出来,跟url的所有key做比较,相等或者是.key ,然后url对应的value还不能是null,这才返回true。
- String[] before,after 这两个属性在排序的时候用到了,before就是表示哪些扩展点在本扩展点前面,after就是表示哪些扩展点在本扩展点后面。
- int order 这个也是在排序的时候使用,先按照 before跟after排,这个排好了直接返回了,最后才用这个order排序。
这里还有个点就是用- 这个符号标识就可以不被激活,在filter属性上使用- 标识需要去掉的过滤器 比如:<dubbo:provider filter="-monitor" /> ,你也用-default来去掉所有。