Dubbo之ExtensionLoader源码解析

这篇具有很好参考价值的文章主要介绍了Dubbo之ExtensionLoader源码解析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 功能概述

  • ExtensionLoader包含了扩展机制的逻辑,类似ClassLoader的功能用途。ExtensionLoader主要功能是加载约定路径下的SPI文件,获取扩展类的类名,并做Class类、实例的缓存。里面还包含自动包装、自动加载、自适应、自动激活等功能的实现逻辑。

2. 功能分析

2.1 ExtensionLoader类分析

2.1.1)主要成员变量分析

/**
 * 扩展接口与ExtensionLoader扩展加载器的映射(类共享变量)
 * 1)包含了ExtensionFactory接口与其它接口的映射
 * 2)每一个SPI接口对应一个ExtensionLoader
 * 3)static 静态成员变量,对象之间共享(具体的对象时,EXTENSION_LOADERS、EXTENSION_INSTANCES等static变量是没有存值的)
 *
 */
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);

/**
 * 扩展接口与扩展实例的映射(类共享变量)
 */
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64);

private final Class<?> type; //扩展接口的类型

private final ExtensionFactory objectFactory; //扩展实例的创建工厂(ExtensionFactory也是SPI接口,1)此处的实例为AdaptiveExtensionFactory@xxx,会用存储的多个扩展工厂查找扩展实例,2)当type=ExtensionFactory.class时,objectFactory=null)

private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>(); //扩展实例类Class与扩展名的映射(该缓存中:多个扩展类可以对应同一个扩展名)

private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>(); //当前扩展接口中的扩展名与扩展类Class的映射(该缓存中,一个扩展名对应一个扩展类,根据加载策略中的overridden值判断是做覆盖还是抛出异常)

private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>(); //扩展名与@Active注解对象的映射
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>(); //扩展名与扩展实例的映射
private final Holder<Object> cachedAdaptiveInstance = new Holder<>(); //自适应扩展类的实例
private volatile Class<?> cachedAdaptiveClass = null; //自适应扩展类(一个扩展接口最多只有一个自适应扩展类)
private String cachedDefaultName; //缓存默认的扩展名,即为SPI上声明的扩展名
private volatile Throwable createAdaptiveInstanceError; //创建自适应扩展实例时发生的错误

private Set<Class<?>> cachedWrapperClasses; //扩展接口对应的封装类集合(封装类不是扩展类,所以没有在cachedClasses缓存中)

private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<>(); //加载时扩展类时的异常信息

private static volatile LoadingStrategy[] strategies = loadLoadingStrategies(); //加载的策略

2.1.2)主要成员方法分析

1)构造方法
private ExtensionLoader(Class<?> type) { //私有的构造方法,创建ExtensionLoader实例(指定扩展接口的类型和扩展工厂)
    this.type = type;
    objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
  • 代码解析:此处获取ExtensionFactory扩展工厂时,采用了递归方法处理,处理逻辑如下
    • 若SPI接口是ExtensionFactory,则objectFactory设置为null,因为自身已经是ExtensionFactory类型了
    • 若SPI接口非ExtensionFactory,则需要objectFactory实例的值,因为ExtensionFactory本身是SPI接口,所以还需要SPI的方式,先获取到ExtensionLoader,再获取自适应的扩展实例
2)获取扩展加载器
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { //获取扩展接口对应的扩展加载器ExtensionLoader(从缓存中获取,若不存在则重新创建)
    if (type == null) { //扩展类型:不为空且是SPI接口
        throw new IllegalArgumentException("Extension type == null");
    }
    if (!type.isInterface()) {
        throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
    }
    if (!withExtensionAnnotation(type)) {
        throw new IllegalArgumentException("Extension type (" + type +
                ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
    }

    ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    if (loader == null) { //每个SPI接口,对应一个扩展加载器ExtensionLoader,若存在直接返回,否则创建ExtensionLoader
        EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
        loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); //直接从Map中获取值,少了中间变量,更简洁些
    }
    return loader;
}
3)获取扩展实例
public T getExtension(String name, boolean wrap) {
    if (StringUtils.isEmpty(name)) {
        throw new IllegalArgumentException("Extension name == null");
    }
    if ("true".equals(name)) { //扩展名为"true",返回默认扩展实例
        return getDefaultExtension();
    }
    final Holder<Object> holder = getOrCreateHolder(name); //获取扩展名对应实例持有类
    Object instance = holder.get();
    if (instance == null) { //使用双重判断+synchronized来确保单实例
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) { //若缓存中没有扩展实例,则创建对应实例对象
                instance = createExtension(name, wrap);
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}
4)创建扩展实例
private T createExtension(String name, boolean wrap) { //创建普通的扩展实例
    // 先加载扩展接口对应的所有扩展类Class,然后在找出扩展名对应扩展类Class
    Class<?> clazz = getExtensionClasses().get(name);
    if (clazz == null) { //配置文件中,若没有查找到扩展名对应的Class类,则抛出扩展发现异常
        throw findException(name);
    }
    try {
        //通过Class的newInstance()创建实例(反射机制)
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); //创建扩展实例,并放入缓存中
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        //注入依赖的扩展实例(类似IOC功能)
        injectExtension(instance);

        if (wrap) { //使用封装类对扩展实例进行封装(类似AOP功能)

            List<Class<?>> wrapperClassesList = new ArrayList<>();
            if (cachedWrapperClasses != null) { //当前扩展接口对应的封装类列表,如WrappedExt的封装类列表为Ext5Wrapper1、Ext5Wrapper2
                wrapperClassesList.addAll(cachedWrapperClasses);
                wrapperClassesList.sort(WrapperComparator.COMPARATOR); //将封装类列表进行排序
                Collections.reverse(wrapperClassesList); //将已经排好序的封装类列表进行翻转
            }

            if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                for (Class<?> wrapperClass : wrapperClassesList) {
                    Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                    /**
                     * 判断是否使用封装类封装扩展实例
                     * a)封装类没有带 @Wrapper注解
                     * b)封装类带有 @Wrapper注解,且扩展名包含在匹配列表matches中,不在不匹配列表mismatches中
                     */
                    if (wrapper == null
                            || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
                        instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); //用封装类封装扩展类的实例
                    }
                }
            }
        }

        initExtension(instance); //若实例为Lifecycle类型,则调用Lifecycle#initialize进行初始化
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                type + ") couldn't be instantiated: " + t.getMessage(), t);
    }
}
5)注入依赖的扩展实例
private T injectExtension(T instance) { //注入依赖的扩展实例

    if (objectFactory == null) { //扩展工厂为空时,提前结束
        return instance;
    }

    try {
        for (Method method : instance.getClass().getMethods()) {
            if (!isSetter(method)) { //只处理set方法
                continue;
            }
            if (method.getAnnotation(DisableInject.class) != null) { //若方法上声明@DisableInject,则不进行注入处理
                continue;
            }
            Class<?> pt = method.getParameterTypes()[0]; //取出set方法中的参数Class
            if (ReflectUtils.isPrimitives(pt)) {
                continue;
            }

            try {
                String property = getSetterProperty(method); //获取属性名(通过解析方法名称)
                Object object = objectFactory.getExtension(pt, property);//此处的objectFactory类型为自适应扩展工厂AdaptiveExtensionFactory,通过遍历其中维护的扩展工厂来获取扩展对象
                if (object != null) { //获取到的扩展实例不为空时,则为对象属性设置值,如Ext6扩展接口的实现类Ext6Impl1
                    method.invoke(instance, object); //使用反射机制调用Set方法,进入扩展对象的依赖注入
                }
            } catch (Exception e) {
                logger.error("Failed to inject via method " + method.getName()
                        + " of interface " + type.getName() + ": " + e.getMessage(), e);
            }

        }
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    }
    return instance; //返回处理后的扩展实例
}
6)加载SPI配置文件
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader,
                          java.net.URL resourceURL, boolean overridden, String... excludedPackages) {
    try { //资源放在try里面创建,不使用时会自动被释放
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {//读取每一行,对每一行进行解析
                final int ci = line.indexOf('#');
                if (ci >= 0) { //若是注释的话,把注释内容去掉,#代表注释
                    line = line.substring(0, ci); //取#号前面的子串
                }
                line = line.trim(); //去除字符串头部、尾部的空格
                if (line.length() > 0) {//@csy-010 解析的时候,怎么去掉扫描行里面的空格?如SimpleExt对应的配置文件,解:调用trim()方法
                    try {
                        String name = null;
                        int i = line.indexOf('='); //按等号进行分隔
                        if (i > 0) { //配置的格式为 extName = className,也可以为 className(不带扩展名)
                            name = line.substring(0, i).trim(); //扩展名(去前后空格)
                            line = line.substring(i + 1).trim(); //扩展类对应的全路径类名(去前后空格)
                        }
                        if (line.length() > 0 && !isExcluded(line, excludedPackages)) { //扩展类的全路径名称,如org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter
                            loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name, overridden); //根据扩展类名产生Class,并加载到缓存中
                        }
                    } catch (Throwable t) { //加载扩展类出现异常时,将异常信息按扩展类的全路径名存起来(某一扩展类加载异常,会把异常信息缓存起来,不影响其它扩展类的加载)
                        IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
                        exceptions.put(line, e);
                    }
                }
            }
        }
    } catch (Throwable t) {
        logger.error("Exception occurred when loading extension class (interface: " +
                type + ", class file: " + resourceURL + ") in " + resourceURL, t);
    }
}
  • 代码解析:加载SPI文件后,会读取扩展配置文件的所有内容,把所有的扩展名与扩展类解析,并依次放入对应的缓存
7)加载扩展类到缓存中
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name,
                       boolean overridden) throws NoSuchMethodException {
    if (!type.isAssignableFrom(clazz)) { //判断实例类是不是接口type的子类型
        throw new IllegalStateException("Error occurred when loading extension class (interface: " +
                type + ", class line: " + clazz.getName() + "), class "
                + clazz.getName() + " is not subtype of interface.");
    }
    if (clazz.isAnnotationPresent(Adaptive.class)) { //缓存自适应扩展类(类上带有@Adaptive注解的扩展类)
        cacheAdaptiveClass(clazz, overridden);
    } else if (isWrapperClass(clazz)) { //缓存封装类型扩展类(即存在包含以扩展接口为参数的构造方法)
        cacheWrapperClass(clazz);
    } else { //缓存自动激活扩展类或普通扩展类
        clazz.getConstructor();
        if (StringUtils.isEmpty(name)) { //什么场景下扩展名为空?解: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); //多个扩展名可以对应一个扩展类,如xxx.Ext10MultiNames配置的内容,如impl,implMultiName=xxx.Ext10MultiNamesImpl
        if (ArrayUtils.isNotEmpty(names)) {
            cacheActivateClass(clazz, names[0]); //缓存扩展名与@Activate对象的映射。有多个扩展名时,取第一个扩展名作为自动激活缓存的key
            for (String n : names) {
                cacheName(clazz, n); //缓存扩展类Class与扩展名的映射(允许多个扩展类Class对应同一个扩展名)
                saveInExtensionClass(extensionClasses, clazz, n, overridden); //缓存扩展名与扩展类Class的映射
            }
        }
    } 
}
  • 代码解析:会加载配置文件中的内容,并设置到不同类型的缓存中,比如cachedAdaptiveClass、cachedWrapperClasses、extensionClasses、cachedActivates等
8)获取自动激活的扩展类列表
public List<T> getActivateExtension(URL url, String[] values, String group) { //获取自动激活的扩展列表(将URL中配置的参数与@Activate配置的内容进行比较)
    List<T> activateExtensions = new ArrayList<>();
    List<String> names = values == null ? new ArrayList<>(0) : asList(values); // 扩展名列表(values是从url中获取的指定key对应的参数值,并按分隔符分隔的值列表)

    /**
     * 类型一:系统激活的扩展类
     */
    if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) { // 系统激活的扩展类处理,即扩展名列表不包含"-default"
        getExtensionClasses(); //加载扩展类。此处没有用到方法的返回值,主要使用方法中的loadExtensionClasses(),值存入成员变量中了,若缓存中没有对应的值,则对应加载并设置到缓存中
        for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) { // 遍历从SPI配置文件中加载的@Activate标识的扩展类列表(需要扩展接口有包含@Active注解的实现类)
            String name = entry.getKey(); //扩展名
            Object activate = entry.getValue(); // @Active对象

            String[] activateGroup, activateValue;

            if (activate instanceof Activate) { //取注解@Activate上设置的group、value值
                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、再比较value)
             * 1)判断传入的group是否在注解中group的列表值中
             * 2)扩展名name不在自定义的列表中(当前处理的扩展类是系统激活的扩展类)
             * 3)将注解中声明的value值与url中参数值进行比较
             * 若都满足条件,则获取扩展名对应的实例,并加载到自动激活扩展的列表中
             */
            if (isMatchGroup(group, activateGroup)
                    && !names.contains(name) //为啥要有这个判断?解答:当前处理的是系统激活的扩展类,而names是自定义的扩展名列表,两者分开处理,所以要进行排除
                    && !names.contains(REMOVE_VALUE_PREFIX + name) //对应场景:在设置扩展条件时,把系统激活的类去除,如"-order",即不加到扩展类列表中
                    && isActive(activateValue, url)) {
                activateExtensions.add(getExtension(name)); // 若匹配,则获取扩展名name对应的实例并加载到列表中
            }
        }
        activateExtensions.sort(ActivateComparator.COMPARATOR); //将可激活扩展类列表进行排序
    }
    List<T> loadedExtensions = new ArrayList<>();

    /**
     * 类型二:自定义激活的扩展类
     */
    for (int i = 0; i < names.size(); i++) {
        String name = names.get(i);
        // 带有排除符号"-"的Filter不加载
        if (!name.startsWith(REMOVE_VALUE_PREFIX)
                && !names.contains(REMOVE_VALUE_PREFIX + name)) { //@csy-007 此处逻辑会在什么场景下进入?解:处理不再cachedActivates缓存中的扩展,如ExtensionLoaderTest.testLoadDefaultActivateExtension
            if (DEFAULT_KEY.equals(name)) { //若指定了"default",则调整对应的顺序
                if (!loadedExtensions.isEmpty()) {
                    activateExtensions.addAll(0, loadedExtensions); //@csy-0014 在指定位置加载列表,原来的值会被覆盖吗?不会覆盖,元素会向后移动
                    loadedExtensions.clear();
                }
            } else {
                loadedExtensions.add(getExtension(name)); //从cachedClasses缓存中获取指定扩展名对应的扩展类,此处若没有查到指定的扩展,是会抛出没找到扩展的异常
            }
        }
    }
    if (!loadedExtensions.isEmpty()) {
        activateExtensions.addAll(loadedExtensions);
    }
    return activateExtensions;
}
  • 代码解析:自动激活的扩展分为两类
    • a)系统激活的扩展类:带有@Activate注解,匹配注解中的group、value获取实例
    • b)自定义激活的扩展类:由用户指定的扩展类,可以不带@Activate注解,不用比较group、value值
9)添加扩展到缓存中
public void addExtension(String name, Class<?> clazz) { //添加扩展接口(通过API方式添加,非配置文件中配置)
    getExtensionClasses(); // load classes(加载扩展类)

    if (!type.isAssignableFrom(clazz)) {
        throw new IllegalStateException("Input type " +
                clazz + " doesn't implement the Extension " + type);
    }
    if (clazz.isInterface()) {
        throw new IllegalStateException("Input type " +
                clazz + " can't be interface!");
    }

    if (!clazz.isAnnotationPresent(Adaptive.class)) { //非自适应扩展类时
        if (StringUtils.isBlank(name)) {
            throw new IllegalStateException("Extension name is blank (Extension " + type + ")!");
        }
        if (cachedClasses.get().containsKey(name)) {
            throw new IllegalStateException("Extension name " +
                    name + " already exists (Extension " + type + ")!");
        }

        cachedNames.put(clazz, name); //符合条件后,设置到缓存中
        cachedClasses.get().put(name, clazz);
    } else { //自适应扩展类,即类上带有@Adaptive注解(一个扩展接口,最多只有一个自适应扩展类)
        if (cachedAdaptiveClass != null) { //添加扩展时,自适应类已存在,则抛出异常
            throw new IllegalStateException("Adaptive Extension already exists (Extension " + type + ")!");
        }

        cachedAdaptiveClass = clazz; //自适应扩展,是不需要处理扩展名的
    }
}

2.2 AdaptiveClassCodeGenerator类分析

2.2.1)主要成员变量分析

private static final Logger logger = LoggerFactory.getLogger(AdaptiveClassCodeGenerator.class);

private static final String CLASSNAME_INVOCATION = "org.apache.dubbo.rpc.Invocation";

private static final String CODE_PACKAGE = "package %s;\n"; //包信息对应的字符串,使用字符串的占位符,来替换具体的值

private static final String CODE_IMPORTS = "import %s;\n"; //导入信息对应的字符串

private static final String CODE_CLASS_DECLARATION = "public class %s$Adaptive implements %s {\n"; //类的声明信息对应的字符串

private static final String CODE_METHOD_DECLARATION = "public %s %s(%s) %s {\n%s}\n"; //方法的声明信息对应的字符串

private static final String CODE_METHOD_ARGUMENT = "%s arg%d"; //方法参数对应的字符串

private static final String CODE_EXTENSION_METHOD_INVOKE_ARGUMENT = "arg%d"; //拼接的内容,用占位符进行处理

private final Class<?> type; //SPI扩展接口

2.2.2)主要成员方法分析

1)自适应类产生
public String generate() { 
    // no need to generate adaptive class since there's no adaptive method found.
    if (!hasAdaptiveMethod()) { //判断是否有自适应方法,如无则抛出异常
        throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
    }

    // 构建自适应实现类:包含package语句、import语句、Class声明、SPI接口中各个方法的对应实现
    StringBuilder code = new StringBuilder();
    code.append(generatePackageInfo()); //把功能点用对应的方法表示,清晰明了,并且使单个方法的内容变少
    code.append(generateImports());
    code.append(generateClassDeclaration());

    Method[] methods = type.getMethods();
    for (Method method : methods) { //遍历SPI接口的方法,并对应产生方法对应的代码
        code.append(generateMethod(method));
    }
    code.append("}");

    if (logger.isDebugEnabled()) {
        logger.debug(code.toString());
    }

    return code.toString();
}
2)产生方法信息
private String generateMethod(Method method) {
    //构建方法:包含方法返回类型、方法名、方法参数、抛出的异常、内容
    String methodReturnType = method.getReturnType().getCanonicalName(); //获取规范的返回类型,如java.lang.String
    String methodName = method.getName();
    String methodContent = generateMethodContent(method); // 产生方法内容
    String methodArgs = generateMethodArguments(method); //如:org.apache.dubbo.common.URL arg0, java.lang.String arg1
    String methodThrows = generateMethodThrows(method);
    // CODE_METHOD_DECLARATION = "public %s %s(%s) %s {\n%s}\n";
    return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent);
}
3)产生方法体内容
private String generateMethodContent(Method method) {
    Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
    StringBuilder code = new StringBuilder(512);
    if (adaptiveAnnotation == null) {
        return generateUnsupported(method); //方法未带@Adaptive注解时,产生不支持操作的描述
    } else {
        int urlTypeIndex = getUrlTypeIndex(method);

        // found parameter in URL type
        if (urlTypeIndex != -1) {
            // Null Point check
            code.append(generateUrlNullCheck(urlTypeIndex)); //产生URL非空检查的语句
        } else {
            // did not find parameter in URL type
            //@csy-011 未找到url参数时的处理逻辑是怎样的?遍历参数Class列表,然后依次查看参数Class对应的方法,判断是否有返回值为URL的get方法,若有则进行相关处理
            code.append(generateUrlAssignmentIndirectly(method)); //间接地通过方法中的参数获取URL,并产生URL非空检查的语句
        }

        String[] value = getMethodAdaptiveValue(adaptiveAnnotation); //获取方法上@Adaptive声明的值

        boolean hasInvocation = hasInvocationArgument(method); //判断方法参数列表中是否存在Invocation类型参数

        code.append(generateInvocationArgumentNullCheck(method)); //产生Invocation类型参数的非空检查的语句

        code.append(generateExtNameAssignment(value, hasInvocation)); //产生获取扩展名的语句(**重点逻辑**)
        // check extName == null?
        code.append(generateExtNameNullCheck(value)); //产生扩展名非空检查的语句

        code.append(generateExtensionAssignment()); //产生获取扩展实例的语句

        // return statement
        code.append(generateReturnAndInvocation(method)); //产生方法调用以及有返回值时返回Return语句
    }

    return code.toString();
}

3. 问题点答疑

  1. ExtensionLoader是只有一个实例,还是会有多个?为什么在ExtensionLoader类描述中,说成是单例的?
  • 解答:实际测试以及代码分析证明,一个扩展接口就会创建一个ExtensionLoader。ExtensionLoader中有许多static成员变量,那这些就是对象共享的,如ConcurrentMap<Class, ExtensionLoader> EXTENSION_LOADERS、 ConcurrentMap<Class, ExtensionLoader> EXTENSION_LOADERS等等。描述中的信息有些断章取义了,经过完整翻译,原意为:被设计为单例或静态(本身完全静态或使用一些静态字段)
  1. Pattern.compile(“\s_[,]+\s_”) 这个正则表达式,代表什么含义?
  • 解答:\s* 表示0个或多个空格,[,]+ 表示一个或多个逗号
  1. ExtensionFactory扩展实例的创建工厂是怎么使用的?默认都是SpiExtensionFactonry,怎样指定扩展工厂?
  • 解答:a)设值:ExtensionFactory是在ExtensionLoader创建时设置的,通过ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()),默认为扩展名spi对应的扩展工厂,即SpiExtensionFactory。 b)使用:在ExtensionLoader#injectExtension注入依赖时,通过扩展工厂获取扩展实例对象
  1. ExtensionLoader#EXTENSION_INSTANCES扩展实例的缓存是在什么时候设置的?
  • 解答:在创建实例的时候ExtensionLoader#createExtension会判断扩展实例对象是否存在,若不存在则会创建,并且设置到缓存中。
  1. ExtensionLoader#cachedAdaptiveInstance(自适应类的实例对象),什么情况下会有值?
  • 解答:在ExtensionLoader#getAdaptiveExtension获取自适应扩展实例时,在实例为空时,会创建自适应实例,并缓存下来
  1. AdaptiveExtensionFactory自适应扩展工厂是怎么被使用上的?为什么叫做自适应?AdaptiveExtensionFactory类上@Adaptive的注解是怎么生效的?
  • 解答:AdaptiveExtensionFactory类上带有@Adaptvie注解,即为ExtensionFactory扩展接口的自适应扩展类。在ExtensionLoader#loadClass时会按@Adaptive进行分类缓存。而AdaptiveExtensionFactory构造方法中,会把SPI配置文件中支持的扩展工厂放到集合中,在进行依赖注入时使用扩展工厂产生扩展实例时,会依次从扩展工厂中创建实例,只要其它一个扩展工厂能创建出实例即可,所以叫做“自适应”
  1. 为什么同一个ExtensionLoader同一个扩展名extName,产生的扩展实例是同一个?
  • 解答:因为ExtensionLoader相同,那么其缓存的cachedClasses扩展类、cachedInstances扩展实例就是相同的,若缓存实例已经创建过,那么就取缓存中的实例,不再重新创建。(同一个扩展接口,对应的ExtensionLoader是相同的)
  1. 默认扩展名ExtensionLoader#cachedDefaultName是怎么读取到值的?
  • 解答:加载扩展类时ExtensionLoader#loadExtensionClasses,会读取扩展接口上的@SPI注解中的value属性值,作为默认扩展名ExtensionLoader#cacheDefaultExtensionName
  1. 封装类是怎么进行缓存的?不放在ExtensionLoader#cachedClasses缓存中吗?
  • 解答:封装类是放在ExtensionLoader#cachedWrapperClasses封装类集合中的,而cachedClasses是普通扩展类的缓存,两者进行分开的。
  1. 解析的配置文件,是怎样进行分类缓存的?
  • 解答:在ExtensionLoader#loadClass方法中,会根据是否带有@Adaptive、@Activate等注解,以及是否为封装类进行缓存。
  1. @Wrapper注解的matches()、mismatches() 匹配和不匹配扩展时怎么使用的?
  • 解答:在ExtensionLoader#createExtension中使用封装类对扩展实例进行封装时,会判断需要创建实例的扩展名name,是否在matches列表中且不在mismatches列表中
  1. ExtensionLoader的类共享变量,什么时候清除的?如:EXTENSION_LOADERS、EXTENSION_INSTANCES?
  • 解答:移除扩展类、扩展类的实例,只在ExtensionLoader#resetExtensionLoader中,但根据该方法的描述,仅用于测试。所以存的这两个共享变量不会主动做清除,只有JVM重启,重新发布服务是,才会失效,然后重新创建
  1. 解析SPI配置文件时,#和空格 是怎么被过滤掉的?
  • 解答:在ExtensionLoader#loadResource中会按"#“、”="做分隔,且会去掉前后空格。
  1. 加载SPI配置文件时,自适应类ExtensionLoader#cachedAdaptiveClass是怎样判断的?
  • 解答:自适应扩展类有两种方式产生,一种是在类上加上@Adaptive,并在SPI配置文件中指定,另一种是SPI扩展接口的方法上加上@Adaptive,产生自适应类对应的字符串,并编译成对应的Class对象,最后再创建出实例。
  1. 自动激活activate的匹配原则是怎样的?
  • 解答:自动激活的扩展类包含两类,一类是系统激活的扩展类,通过group、value等进行比较(可通过default来指定系统扩展类),另一类是用户自定义扩展类,只要指定扩展名即可,不需要匹配group、value
  1. 使用Holder来存储对象,比直接使用对象,好处在哪里?
  • 解答:Holder从字面上解释为“持有类”,用于持有目标对象。好处就是对目标对象进行封装和抽象,可以做更多的逻辑。
  1. 加载策略LoadingStrategy中的overridden是否覆盖,有什么用途?
  • 解答:在ExtensionLoader#saveInExtensionClass保存扩展名与扩展类的缓存时,遇到一个扩展名对应多个扩展类时,会根据overridden来判断做覆盖或者抛出异常。
  1. @Activate#value自动激活中的value是指url中的key,还是扩展名? value值如"key1:value2,key2:value2",是怎么比较的?
  • 解答:value指的是url中key对应的value值,是指扩展名。
  1. SPI配置文件中没有配置扩展名时,怎样创建默认扩展名?如:org.apache.dubbo.common.extension.activate.ActivateExt1文件中的xxx.ActivateExt1Impl1没有扩展名,最终的缓存名字为"activateExt1Impl1"
  • 解答:
    • 1)判断扩展类型上是否带有注解@Extension,若有去注解中的value值(@Extension已被废弃,不推荐使用)
    • 2)获取扩展类的简易名称,如org.apache.dubbo.common.extension.activate.impl.ActivateExt1Impl1的getSimpleName()为"ActivateExt1Impl1"
      • a)扩展类的简易名称以扩展接口的简易名称为后缀,如xxxActivateExt1,则把扩展接口的简易名称去掉,即取前半部分的子字符串,得到的扩展名为"xxx"
      • b)若不是a)中的形式,则直接将扩展名的简易名称直接小写作为扩展名,如ActivateExt1Impl1对应为“activateext1impl1”
  1. ExtensionLoader#cacheActivateClass缓存的是扩展名与@Activate对象的映射,在使用时,是怎么找到带有@Activate注解的自动激活扩展类的?
  • 解答:自动激活的缓存类型为Map<String, Object> cachedActivates,即缓存了扩展名与@Active注解对象的映射,在获取自动激活的扩展类时,即ExtensionLoader#getActivateExtension会将缓存中@Activate注解配置的值,与输入的参数进行匹配,若匹配成功,则通过@Activate关联扩展名调用getExtension(name),获取到最终的扩展实例

4. 归纳总结

  1. 不管是自适应扩展类@Adaptive、还是自动激活扩展类@Activate,最终都是找到明确的扩展名,然后通过getExtension(name)找到扩展实例
    不过查找扩展名方式有些不同,
    a)@Adaptive(value={“key1”,“key2”}),最终是通过 url.getParameter(“key1”,getParameter(“key2”,defaultExtName)) 从url中查找到指定参数对应的扩展名,
    b)@Activate有两种形式,一种是自定义激活,另一种是系统自动激活
    b.1)自定义激活:ExtensionLoader#getActivateExtension(URL url, String key),直接将url中key对应的值作为扩展名,不需要带上@Activate注解
    b.2)自动自动激活:对@Activate注解中的group、value进行比较,满足条件了,就使用@Activate注解对应的扩展名,有两种形式
    b.2.1)如@Activate(value={key1,key2}, group=“default_group”),则满足group匹配且,key1、key2出现在url参数的key中。
    b.2.2)如@Activate(value={key1:value1,key2:value2}, group=“default_group”),则满足group匹配且,key1、value1或key2、value2任意一个键值对同时出现在url参数中,
    即该@Activate注解对应的实例被匹配,然后再到缓存中找到注解对应的扩展名

  2. 获取实例时,getExtension、getAdaptiveExtension、getActivateExtensionsion获取实例时,都会先调用getExtensionClasses()把扩展类加到缓存中(若已经加载过,则从缓存中取)。getExtensionClasses()中的loadClass()能够分类存储。

  3. @Activate自动激活扩展类进行匹配时,group和value条件只要没配置,就表明不使用group、value做限制,即匹配所有。如group没有配置,那么所有group扩展类都能匹配;没有配置value,那么所有value的扩展类都能匹配。

  4. 创建扩展类实例时,默认是会自动注入依赖的,若不想自动注册依赖,在setXxx方法上加上@DisableInject注解。

  5. 一个扩展名只能对应一个扩展类,一个扩展类可以对应多个扩展名,即扩展类与扩展名时是一对多的关系,在配置文件中,扩展名可以配置多个,用逗号分隔。文章来源地址https://www.toymoban.com/news/detail-650865.html

到了这里,关于Dubbo之ExtensionLoader源码解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 从源码全面解析 dubbo 消费端服务调用的来龙去脉

    👏作者简介:大家好,我是爱敲代码的小黄,独角兽企业的Java开发工程师,CSDN博客专家,阿里云专家博主 📕系列专栏:Java设计模式、Spring源码系列、Netty源码系列、Kafka源码系列、JUC源码系列、duubo源码系列 🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦

    2024年02月09日
    浏览(37)
  • 从源码全面解析 dubbo 服务端服务调用的来龙去脉

    👏作者简介:大家好,我是爱敲代码的小黄,独角兽企业的Java开发工程师,CSDN博客专家,阿里云专家博主 📕系列专栏:Java设计模式、Spring源码系列、Netty源码系列、Kafka源码系列、JUC源码系列、duubo源码系列 🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦

    2024年02月11日
    浏览(33)
  • Dubbo源码解析第一期:如何使用Netty4构建RPC

            早期学习和使用Dubbo的时候(那时候Dubbo还没成为Apache顶级项目),写过一些源码解读,但随着Dubbo发生了翻天覆地的变化,那些文章早已过时,所以现在计划针对最新的Apache Dubbo源码来进行“阅读理解”,希望和大家一起再探Dubbo的实现。由于能力有限,如果文章

    2024年01月21日
    浏览(37)
  • 【Dubbo】Dubbo负载均衡实现解析

    📫作者简介: 小明java问道之路 , 2022年度博客之星全国TOP3 ,专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化,文章内容兼具广度、深度、大厂技术方案,对待技术喜欢推理加验证,就职于知名金融公司后端高级工程师。          📫 热衷分享,喜欢原

    2024年02月05日
    浏览(48)
  • Dubbo 3.x源码(16)—Dubbo服务发布导出源码(5)

    基于Dubbo 3.1,详细介绍了Dubbo服务的发布与引用的源码。 此前我们学习了Dubbo 3.x源码(15)—Dubbo服务发布导出源码(4),也就是Dubbo远程服务导出export方法的上半部分,也就是doLocalExport源码,将会得到一个Exporter。 现在我们继续学习,在导出远程服务得到Exporter之后,继续通过R

    2024年01月25日
    浏览(46)
  • Dubbo源码浅析(一)—RPC框架与Dubbo

    RPC,Remote Procedure Call 即远程过程调用,与之相对的是本地服务调用,即LPC(Local Procedure Call)。本地服务调用比较常用,像我们应用内部程序 (注意此处是程序而不是方法,程序包含方法) 互相调用即为本地过程调用,而远程过程调用是指在本地调取远程过程进行使用。 而 RPC框

    2024年02月08日
    浏览(41)
  • 08 dubbo源码学习_LoadBalance

    它的职责是将网络请求,或者其他形式的负载“均摊”到不同的机器上。避免集群中部分服务器压力过大,而另一些服务器比较空闲的情况。通过负载均衡,可以让每台服务器获取到适合自己处理能力的负载 入口是在AbstractClusterInvoker中,这个抽象类要上一篇中已经讲过,它

    2023年04月26日
    浏览(33)
  • dubbo源码阅读之-java spi, dubbo spi 和 Spring spi 到底有啥区别

    SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。

    2024年02月08日
    浏览(41)
  • Dubbo源码篇06---SPI神秘的面纱---原理篇---上

    上一篇文章,Dubbo源码篇05—SPI神秘的面纱—使用篇带领大家过了一遍Dubbo SPI的机制和使用,本文我们来深入Dubbo源码,一览背后原理。 本文基于: Dubbo SPI机制解析 一文而做,简化原文源码篇幅,加以图画解释。 随着服务化的推广,网站对Dubbo服务框架的需求逐渐增多,Dub

    2024年02月07日
    浏览(43)
  • dubbo源码中设计模式——负载均衡中模版模式的应用

    在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。 使用场景:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板

    2024年02月19日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包