gpt4 book ai didi

Dubbo-Activate实现原理

转载 作者:我是一只小鸟 更新时间:2022-11-20 22:31:35 26 4
gpt4 key购买 nike

前言

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

如何使用

  1. 自定义接口;
                          
                          
                          
                             @SPI 
                            
public   interface   ActivateDemo   {

     /**
     * 测试
     *  @param  msg
     *  @return
     */

     String  test (String msg) ;

}
  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. 在resources下新建META-INF/dubbo/internal文件夹,新建自己定义接口的全限定名文件名,名称以及内容可参考以下内容;
image.png
image.png
  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()));
    
    }
                          
                          
                          
                                
                              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 三个属性,三个属性作用如下

  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 ;
}

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;
    }

结束

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

最后此篇关于Dubbo-Activate实现原理的文章就讲到这里了,如果你想了解更多关于Dubbo-Activate实现原理的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com