• Dubbo-Activate实现原理


    前言

    在Dubbo中有Filter使用,对于Filter来说我们会遇到这样的问题,Filter自身有很多的实现,我们希望某种条件下使用A实现,另外情况下使用B实现,这个时候我们前面介绍@SPI和@Adaptive就不能满足我们要求了,这个时候我们就需要使用@Activate。
    Activate注解表示一个扩展是否被激活(使用),可以放在类定义和方法上,Dubbo中用它在扩展类定义上,表示这个扩展实现激活条件和时机。

    如何使用

    1. 自定义接口;
    @SPI
    public interface ActivateDemo {
    
        /**
         * 测试
         * @param msg
         * @return
         */
        String test(String msg);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 实现接口,分别进行默认实现、多个组、排序、从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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    1. 在resources下新建META-INF/dubbo/internal文件夹,新建自己定义接口的全限定名文件名,名称以及内容可参考以下内容;

    ![image.png](https://img-blog.csdnimg.cn/img_convert/47e9851c923a3dabe7101cf818c1843e.png#clientId=u7068ce0c-8c73-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=229&id=uf6cffa34&margin=[object Object]&name=image.png&originHeight=458&originWidth=2908&originalType=binary&ratio=1&rotation=0&showTitle=false&size=170692&status=done&style=none&taskId=u4c55c7d1-232a-4062-b4cf-9d98cb5c2e8&title=&width=1454)

    1. 测试案例;
        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()));
        
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
        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()));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    源码分析

    @Activate注解标注在扩展实现类上,有 group、value 以及 order 三个属性,三个属性作用如下:

    1. group 修饰的实现类可以列举为一种标签,标签用来区分是在 Provider 端被激活还是在 Consumer 端被激活;
    2. value 修饰的实现类只在 URL 参数中出现指定的 key 时才会被激活;
    3. 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    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);
                    }
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    使用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;
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    结束

    欢迎大家点点关注,点点赞!

  • 相关阅读:
    什么是APS生产排程系统?
    Vue——vue3中的ref和reactive数据理解以及父子组件之间props传递的数据
    [附源码]计算机毕业设计JAVA高校知识产权管理系统论文2022
    qt QTreeWidget 学习
    手把手实现简易版vue(二)组件类解析
    始祖双碳新闻 | 2022年8月1号-8月5号碳中和行业早知道
    Linux fdisk实战
    【小程序源码】uni-app云开发的网盘助手抓取网盘资源
    后端面试话术集锦第 十八 篇:JVM面试话术
    两个栈实现一个队列
  • 原文地址:https://blog.csdn.net/weixin_38592881/article/details/126922780