深度解析dubbo扩展技术dubbo spi(自动激活实现)

注:本文基于dubbo v2.6.1

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有多个属性,我们再来回顾一下

  1. String[] group ,这个属性是分组的,在我们服务提供者就是provider,然后服务消费者那边就是consumer,你没配置组也就算了,如果你配置了组,就会把不是你这个组的给过滤掉。
  2. String[] value ,这个参数也是个数组形式,他会查找url中如果包含该key值就会,就会激活。咱们上面代码中也看到了判断中有个isActive(activate, url) ,其实这个方法就是把activate的value值拿出来,跟url的所有key做比较,相等或者是.key ,然后url对应的value还不能是null,这才返回true。
  3. String[] before,after 这两个属性在排序的时候用到了,before就是表示哪些扩展点在本扩展点前面,after就是表示哪些扩展点在本扩展点后面。
  4. int order 这个也是在排序的时候使用,先按照 before跟after排,这个排好了直接返回了,最后才用这个order排序。
    这里还有个点就是用- 这个符号标识就可以不被激活,在filter属性上使用- 标识需要去掉的过滤器 比如:<dubbo:provider filter="-monitor" /> ,你也用-default来去掉所有。

猜你喜欢

转载自blog.csdn.net/yuanshangshenghuo/article/details/106437399