DubboSPI使用以及使用方式源码解读

news2024/11/17 19:46:40

DubboSPI流程图

img

为什么Dubbo要自己实现SPI

java spi 会一次性实例化扩展点所有实现,机制并不能根据获取自己想要的类

获取一个类的实现对象

javaSPI 具有局限性:
1、没法给实现类起别名
2、没法实现包装类。类似AOP的原理
3、没法实现自动注入
4、没法实现按需加载。一次性就会加载配置文件中配置的所有类

Dubbo SPI的改进

根据需要实例化扩展点

增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。

DubboSPI使用

1、@SPI 注解需要打在接口上(“可以传入value”,默认找的实现类)

2、META-INFO.dubbo下需要创建一个与接口全类名相同的文件

3、配置文件内需要配置实现类全类名

4、Wrapper类(类似AOP)需要通过有参构造方法来注入

Wrapper

Dubbo通过Wrapper实现AOP的方法

Wrapper机制,即扩展点自动包装。Wrapper 类同样实现了扩展点接口,但是 Wrapper 不是扩展点的真正实现。它的用途主要是用于从 ExtensionLoader 返回扩展点时,包装在真正的扩展点实现外。即从 ExtensionLoader 中返回的实际上是 Wrapper 类的实例,Wrapper 持有了实际的扩展点实现类。 扩展点的 Wrapper 类可以有多个,也可以根据需要新增。 通过 Wrapper 类可以把所有扩展点公共逻辑移至 Wrapper 中。新加的 Wrapper 在所有的扩展点上添加了逻辑,有些类似 AOP,即 Wrapper 代理了扩展点。

Wrapper的规范 Wrapper 机制不是通过注解实现的,而是通过一套 Wrapper 规范实现的。 Wrapper 类在定义时需要遵循如下规范。

  • 该类要实现 SPI 接口
  • 该类中要有 SPI 接口的引用
  • 该类中必须含有一个含参的构造方法且参数只能有一个类型为SPI接口
  • 在接口实现方法中要调用 SPI 接口引用对象的相应方法
  • 该类名称以 Wrapper 结尾

Dubbo 3.1.2 版本spi源码阅读

dubbo 3版本对spi做了优化,在以前版本的基础上,对spi添加了模块化工作范围(scope)

借助SPI实现相关探针,使得生命周期可以被容器感知,即生命周期与容器对齐。

大致梳理流程:

1、根据指定的接口加载配置文件

2、根据配置文件加载对应的类 缓存起来

3、根据加载的类按需反射生成对象 缓存起来

4、完成自动注入

ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Test.class);

dubbo3版本使用**ExtensionLoader**更换成 **ApplicationModel.defaultModel().getExtensionLoader(Test.class)**根据工作范围获取ExtensionLoader

getExtension

public T getExtension(String name) {
    //多传了个参数wrap为true调用另外一个重载的方法
    T extension = getExtension(name, true);
    if (extension == null) {
        throw new IllegalArgumentException("Not find extension: " + name);
    }
    return extension;
}
public T getExtension(String name, boolean wrap) {
		//检查扩展加载器是否已被销毁
        checkDestroyed();
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        //扩展名字为true则加载默认扩展
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        //非wrap类型则将缓存的扩展名字key加上_origin后缀
        //wrap是aop机制 这个origin在aop里面可以称为切点,下面的wrap扩展可以称为增强通知的类型,普通扩展和wrap扩展的扩展名字是一样的
        String cacheKey = name;
        if (!wrap) {
            cacheKey += "_origin";
        }
        //从cachedInstances缓存中查询
        final Holder<Object> holder = getOrCreateHolder(cacheKey);
        Object instance = holder.get();
        //缓存中不存在则创建扩展对象 双重校验锁
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                	//创建扩展对象
                    instance = createExtension(name, wrap);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

加载默认扩展 getDefaultExtension()

public T getDefaultExtension() {
    //加载扩展类型对应的所有扩展SPI实现类型,在加载所有扩展实现类型的时候会缓存这个扩展的默认实现类型
    //将对象缓存在cachedDefaultName中
    getExtensionClasses();
    // cachedDefaultName 是否已经更新或者为true,避免死循环
    if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) {
        return null;
    }
    // 根据 cachedDefaultName 加载
    return getExtension(cachedDefaultName);
}

getExtensionClasses()

private Map<String, Class<?>> getExtensionClasses() {
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    try {
                        // 核心,加载类, 解析在下面
                        classes = loadExtensionClasses();
                    } catch (InterruptedException e) {
                        logger.error(COMMON_ERROR_LOAD_EXTENSION, "", "", "Exception occurred when loading extension class (interface: " + type + ")", e);
                        throw new IllegalStateException("Exception occurred when loading extension class (interface: " + type + ")", e);
                    }
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }

createExtension(name, wrap);

private T createExtension(String name, boolean wrap) {
    //扫描所有jar中的实现,获取对应扩展名字的Class对象  getExtensionClasses()解释在上面
    Class<?> clazz = getExtensionClasses().get(name);
    if (clazz == null || unacceptableExceptions.contains(name)) {
        throw findException(name);
    }
    try {
        // 如果创建过了clazz对象 直接从缓存中取
        T instance = (T) extensionInstances.get(clazz);
        if (instance == null) {
            // 第一次创建扩展对象后,缓存起来,对应上面从缓存中取
            extensionInstances.putIfAbsent(clazz, createExtensionInstance(clazz));
            instance = (T) extensionInstances.get(clazz);
            instance = postProcessBeforeInitialization(instance, name);
            // 注入自适应扩展方法
            injectExtension(instance);
            instance = postProcessAfterInitialization(instance, name);
        }
    	// AOP实现warp 代理类 ,默认开启
        if (wrap) {
            List<Class<?>> wrapperClassesList = new ArrayList<>();
            if (cachedWrapperClasses != null) {
               	wrapperClassesList.addAll(cachedWrapperClasses);
                wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                Collections.reverse(wrapperClassesList);
            }

            if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                for (Class<?> wrapperClass : wrapperClassesList) {
                    Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                    // wrapper 不存在 
                    // matches 包含name 
                    // mismatches不包含当前扩展 name
                    boolean match = (wrapper == null) ||
                        ((ArrayUtils.isEmpty(wrapper.matches()) || ArrayUtils.contains(wrapper.matches(), name)) &&
                            !ArrayUtils.contains(wrapper.mismatches(), name));
                    // 是否需要注入
                    if (match) {
                        // 核心 创建wrapper 类型对象, 然后根据构造函数注入,然后返回给instance 
                    	// newInstance 创建对象时,如果有参数构造函数,直接传入参数,就实现了根据有参构造创建对象
                        //  实现 返回的instance 编程包装类型
                        instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                        instance = postProcessAfterInitialization(instance, name);
                    }
                }
            }
        }

        // Warning: After an instance of Lifecycle is wrapped by cachedWrapperClasses, it may not still be Lifecycle instance, this application may not invoke the lifecycle.initialize hook.
        // 如果 instance 是Lifecycle 类型的对象 ,执行 Lifecycle 的  initialize(); 方法
        initExtension(instance);
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
            type + ") couldn't be instantiated: " + t.getMessage(), t);
    }
private void initExtension(T instance) {
        if (instance instanceof Lifecycle) {
            Lifecycle lifecycle = (Lifecycle) instance;
            lifecycle.initialize();
        }
    }

injectExtension(T instance) 依赖注入

private T injectExtension(T instance) {
        if (injector == null) {
            return instance;
        }

        try {
            for (Method method : instance.getClass().getMethods()) {
                // 不是以set开头 方法 返回
                if (!isSetter(method)) {
                    continue;
                }
                /**
                 * Check {@link DisableInject} to see if we need auto-injection for this property
                 */
                // 如果有 禁用注入注解 DisableInject 结束
                if (method.isAnnotationPresent(DisableInject.class)) {
                    continue;
                }

                // When spiXXX implements ScopeModelAware, ExtensionAccessorAware,
                // the setXXX of ScopeModelAware and ExtensionAccessorAware does not need to be injected
                if (method.getDeclaringClass() == ScopeModelAware.class) {
                    continue;
                }
                if (instance instanceof ScopeModelAware || instance instanceof ExtensionAccessorAware) {
                    if (ignoredInjectMethodsDesc.contains(ReflectUtils.getDesc(method))) {
                        continue;
                    }
                }

                Class<?> pt = method.getParameterTypes()[0];
                if (ReflectUtils.isPrimitives(pt)) {
                    continue;
                }

                try {
                    String property = getSetterProperty(method);
                    Object object = injector.getInstance(pt, property);
                    if (object != null) {
                        method.invoke(instance, object);
                    }
                } catch (Exception e) {
                    logger.error(COMMON_ERROR_LOAD_EXTENSION, "", "", "Failed to inject via method " + method.getName()
                        + " of interface " + type.getName() + ": " + e.getMessage(), e);
                }
            }
        } catch (Exception e) {
            logger.error(COMMON_ERROR_LOAD_EXTENSION, "", "", e.getMessage(), e);
        }
        return instance;
    }

怎么实现的Wrapper包装

java 反射基础知识

createExtension方法 中 instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));newInstance(instance) 即 根据有参构造方法,同时传入 instance 创建对象,以次方式实现了用构造方法传入包装对象

LoadingStrategy 及 loadExtensionClasses()

LoadingStrategy 是 加载策略 在 loadExtensionClasses() 加载配置文件方法中的优化使用

loadExtensionClasses()

dubbo 以前加载配置文件时通过如下方式

private Map<String, Class<?>> loadExtensionClasses() {
    cacheDefaultExtensionName();
    Map<String, Class<?>> extensionClasses = new HashMap<>();
    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
    loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
    loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
    loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
    loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
    return extensionClasses;
}

加载三个位置的 “META-INF/dubbo/internal/”,“META-INF/dubbo/”,“META-INF/services/” 文件

dubbo通过 LoadingStrategy 定义了一个加载策略,通过遍历加载策略的形式,去加载spi扩展文件,实现了加载扩展文件的可扩展性 (不是dubbo3的优化)

private Map<String, Class<?>> loadExtensionClasses() throws InterruptedException {
    checkDestroyed();
    cacheDefaultExtensionName();

    Map<String, Class<?>> extensionClasses = new HashMap<>();
    // 遍历加载策略类
    for (LoadingStrategy strategy : strategies) {
        loadDirectory(extensionClasses, strategy, type.getName());

        // compatible with old ExtensionFactory
        // 兼容旧版本
        if (this.type == ExtensionInjector.class) {
            loadDirectory(extensionClasses, strategy, ExtensionFactory.class.getName());
        }
    }

    return extensionClasses;
}

加载LoadingStrategy

LoadingStrategy 是采用java的spi方式

private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
private static LoadingStrategy[] loadLoadingStrategies() {
        // 通过ServiceLoader.load方法  (java spi 方式)来加载LoadingStrategy的扩展
        return stream(load(LoadingStrategy.class).spliterator(), false)
            .sorted()
            .toArray(LoadingStrategy[]::new);
}

META-INF/services/org.apache.dubbo.common.extension.LoadingStrategy

org.apache.dubbo.common.extension.DubboInternalLoadingStrategy
org.apache.dubbo.common.extension.DubboLoadingStrategy
org.apache.dubbo.common.extension.ServicesLoadingStrategy

img

loadDirectoryInternal 方法详解

fileName就是LoadingStrategy所指定的目录 + 接口的全限定名,这里就解释了为什么实现类需要写在类全限定名的文件里

private void loadDirectoryInternal(Map<String, Class<?>> extensionClasses, LoadingStrategy loadingStrategy, String type) throws InterruptedException {
    //LoadingStrategy所指定的目录 + 接口的全限定名
    String fileName = loadingStrategy.directory() + type;
    try {
        List<ClassLoader> classLoadersToLoad = new LinkedList<>();
        
        // try to load from ExtensionLoader's ClassLoader first
        // 是否需要在加载ExtensionLoader之前 先用 ClassLoader 加载 
       
        if (loadingStrategy.preferExtensionClassLoader()) {
            ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
            if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
                classLoadersToLoad.add(extensionLoaderClassLoader);
            }
        }

        //有些spi只通过dubbo框架实现,扫描多类加载器资源可能会导致应用程序启动非常慢
        if (specialSPILoadingStrategyMap.containsKey(type)) {
            String internalDirectoryType = specialSPILoadingStrategyMap.get(type);
            //skip to load spi when name don't match
            if (!LoadingStrategy.ALL.equals(internalDirectoryType)
                && !internalDirectoryType.equals(loadingStrategy.getName())) {
                return;
            }
            classLoadersToLoad.clear();
            classLoadersToLoad.add(ExtensionLoader.class.getClassLoader());
        } else {
            // load from scope model
            Set<ClassLoader> classLoaders = scopeModel.getClassLoaders();

            if (CollectionUtils.isEmpty(classLoaders)) {
                // getSystemResources 去指定的文件, 加载返回一个URL对象 
                Enumeration<java.net.URL> resources = ClassLoader.getSystemResources(fileName);
                if (resources != null) {
                    while (resources.hasMoreElements()) {
                        // 根据 '=' 切割 然后加载类
                        loadResource(extensionClasses, null, resources.nextElement(), loadingStrategy.overridden(),
                            loadingStrategy.includedPackages(),
                            loadingStrategy.excludedPackages(),
                            loadingStrategy.onlyExtensionClassLoaderPackages());
                    }
                }
            } else {
                classLoadersToLoad.addAll(classLoaders);
            }
        }

        Map<ClassLoader, Set<java.net.URL>> resources = ClassLoaderResourceLoader.loadResources(fileName, classLoadersToLoad);
        resources.forEach(((classLoader, urls) -> {
            loadFromClass(extensionClasses, loadingStrategy.overridden(), urls, classLoader,
                loadingStrategy.includedPackages(),
                loadingStrategy.excludedPackages(),
                loadingStrategy.onlyExtensionClassLoaderPackages());
        }));
    } catch (InterruptedException e) {
        throw e;
    } catch (Throwable t) {
        logger.error(COMMON_ERROR_LOAD_EXTENSION, "", "", "Exception occurred when loading extension class (interface: " +
            type + ", description file: " + fileName + ").", t);
    }
}

loadresource()=>loadClass(

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name,
                           boolean overridden) {
        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.");
        }
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            // 如果类有Adaptive注解,缓存类,优先级最高
            cacheAdaptiveClass(clazz, overridden);
        } else if (isWrapperClass(clazz)) {
            // 如果是包装类 缓存Wrapper
            cacheWrapperClass(clazz);
        } else {
            if (StringUtils.isEmpty(name)) {
                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)) {
                // 如果类中包含 Activate 注解 ,添加到缓存
                cacheActivateClass(clazz, names[0]);
                for (String n : names) {
                    cacheName(clazz, n);
                    // 类名和实例,存入之前的map
                    saveInExtensionClass(extensionClasses, clazz, n, overridden);
                }
            }
        }
    }

Dubbo中的Adaptive机制源码解析

获取方法ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()

@Adaptive 放在类上

如果找到了@Adaptive标注的实现类,直接返回然后实例化 在getAdaptiveExtensionClass() 源码中有解释

如果没有找到@Adaptive标注的实现类,则会通过代理生成一个类并实例化返回

@Adaptive 放在方法上

上面如果没有找到@Adaptive标注的类。则会去动态代理生成一个类。@Adaptive在方法上的作用就体现出来了

1、加了@Adaptive 的方法会被代理。其他的方法则默认方法体都是抛异常

2、加了@Adaptive 的方法的形参上必须有URL类型的参数或者对象有getURL的方法。可以看代理类的源代码。

getAdaptiveExtension()

public T getAdaptiveExtension() {
        checkDestroyed();
        Object instance = cachedAdaptiveInstance.get();
        if (instance == null) {
            if (createAdaptiveInstanceError != null) {
                throw new IllegalStateException("Failed to create adaptive instance: " +
                    createAdaptiveInstanceError.toString(),
                    createAdaptiveInstanceError);
            }

            synchronized (cachedAdaptiveInstance) {
                instance = cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
                        // 核心 先从缓存获取,缓存没有就创建
                        instance = createAdaptiveExtension();
                        cachedAdaptiveInstance.set(instance);
                    } catch (Throwable t) {
                        createAdaptiveInstanceError = t;
                        throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
                    }
                }
            }
        }

        return (T) instance;
    }
private T createAdaptiveExtension() {
        try {
            T instance = (T) getAdaptiveExtensionClass().newInstance();
            instance = postProcessBeforeInitialization(instance, null);
            injectExtension(instance);
            instance = postProcessAfterInitialization(instance, null);
            initExtension(instance);
            return instance;
        } catch (Exception e) {
            throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }

getAdaptiveExtensionClass()

private Class<?> getAdaptiveExtensionClass() {
    // 去加载扩展类
    getExtensionClasses();
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    // 如果没有adaptive 注解修饰的类,用动态代理创建一个代理类
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

Protocol生成代理类内容

package org.apache.dubbo.rpc;

import java.util.List;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.Exporter;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Protocol;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.model.ScopeModel;
import org.apache.dubbo.rpc.model.ScopeModelUtil;

public class Protocol$Adaptive implements Protocol {
    @Override
    public void destroy() {
        throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }

    @Override
    public int getDefaultPort() {
        throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }

    public List getServers() {
        throw new UnsupportedOperationException("The method public default java.util.List org.apache.dubbo.rpc.Protocol.getServers() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }

    public Exporter export(Invoker invoker) throws RpcException {
        String string;
        if (invoker == null) {
            throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
        }
        if (invoker.getUrl() == null) {
            throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
        }
        URL uRL = invoker.getUrl();
        String string2 = string = uRL.getProtocol() == null ? "dubbo" : uRL.getProtocol();
        if (string == null) {
            throw new IllegalStateException(new StringBuffer().append("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (").append(uRL.toString()).append(") use keys([protocol])").toString());
        }
        ScopeModel scopeModel = ScopeModelUtil.getOrDefault(uRL.getScopeModel(), Protocol.class);
        Protocol protocol = scopeModel.getExtensionLoader(Protocol.class).getExtension(string);
        return protocol.export(invoker);
    }

    public Invoker refer(Class clazz, URL uRL) throws RpcException {
        String string;
        if (uRL == null) {
            throw new IllegalArgumentException("url == null");
        }
        URL uRL2 = uRL;
        String string2 = string = uRL2.getProtocol() == null ? "dubbo" : uRL2.getProtocol();
        if (string == null) {
            throw new IllegalStateException(new StringBuffer().append("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (").append(uRL2.toString()).append(") use keys([protocol])").toString());
        }
        ScopeModel scopeModel = ScopeModelUtil.getOrDefault(uRL2.getScopeModel(), Protocol.class);
        Protocol protocol = scopeModel.getExtensionLoader(Protocol.class).getExtension(string);
        return protocol.refer(clazz, uRL);
    }
}

@Activte激活拓展点

有点像Spring当中的Conditional

@ConditionalOnBean(XXX.class)只有XXX这个类在容器中存在,才会执行修饰的方法

  • group() 指定组条件。框架SPI定义了有效的组值。
  • value() 指定URL条件中的参数键。
@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;

使用

@Activate(group = {Constants.PROVIDER,Constants.CONSUMER},order = 5,value = “value”)

ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Filter.class);
URL url=new URL("","",3);
url=url.addParameter("key", "value");
List list=extensionLoader.getActivateExtension(url,"key",Constants.PROVIDER);

url 中是否有 满足有values 的参数

是否满足group 相等

注意点,如果一个类Activate 注解中 中的同时添加了value 和group 那么必须要同时满足

group分组的满足条件

  • 获取扩展点时没传group 默认满足条件
  • Activate注解中的groups 不为空且包含传入的group时 满足条件

源码

getActivateExtension(URL url, String key, String group)

/**
 *  
 *
 * @param url   url
 * @param key   获取url参数键 
 * @param group group 用于筛选的分组,比如使用此参数来区分消费者还是提供者(consumer,provider)
 * @return 已激活的扩展列表。
 */
public List<T> getActivateExtension(URL url, String key, String group) {
    //获取url指定的参数值
    String value = url.getParameter(key);
    //调用重载的方法
    return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group);
}

getActivateExtension(URL url, String[] values, String group)

public List<T> getActivateExtension(URL url, String[] values, String group) {
        checkDestroyed();
        // solve the bug of using @SPI's wrapper method to report a null pointer exception.
    //TreeMap 有序即可,对扩展排序
    Map<Class<?>, T> activateExtensionsMap = new TreeMap<>(activateComparator);
        List<String> names = values == null ? new ArrayList<>(0) : asList(values);
        Set<String> namesSet = new HashSet<>(names);
        if (!namesSet.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
            if (cachedActivateGroups.size() == 0) {
                synchronized (cachedActivateGroups) {
                    // cache all extensions
                    if (cachedActivateGroups.size() == 0) {
                        // 加载对应扩展类
                        getExtensionClasses();
                        for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
                            String name = entry.getKey();
                            Object activate = entry.getValue();

                            String[] activateGroup, activateValue;
                        	// 遍历之前缓存的map  获取group 和value
                            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;
                            }
                            // 根据扩展名称缓存分组
                            cachedActivateGroups.put(name, new HashSet<>(Arrays.asList(activateGroup)));
                            String[][] keyPairs = new String[activateValue.length][];
                            for (int i = 0; i < activateValue.length; i++) {
                                if (activateValue[i].contains(":")) {
                                    keyPairs[i] = new String[2];
                                    String[] arr = activateValue[i].split(":");
                                    keyPairs[i][0] = arr[0];
                                    keyPairs[i][1] = arr[1];
                                } else {
                                    keyPairs[i] = new String[1];
                                    keyPairs[i][0] = activateValue[i];
                                }
                            }
                            cachedActivateValues.put(name, keyPairs);
                        }
                    }
                }
            }

            // traverse all cached extensions
            //遍历所有激活的扩展名字和扩展分组集合
            cachedActivateGroups.forEach((name, activateGroup) -> {
                //筛选当前扩展的扩展分组与激活扩展的扩展分组是否可以匹配
                if (isMatchGroup(group, activateGroup)
                    && !namesSet.contains(name)
                    && !namesSet.contains(REMOVE_VALUE_PREFIX + name)
//如果在Active注解中配置了value则当指定的键出现在URL的参数中时,激活当前扩展名。 
//如果未配置value属性则默认都是匹配的(cachedActivateValues中不存在对应扩展名字的缓存的时候默认为true
                    && isActive(cachedActivateValues.get(name), url)) {

                    activateExtensionsMap.put(getExtensionClass(name), getExtension(name));
                }
            });
        }

        if (namesSet.contains(DEFAULT_KEY)) {
            // will affect order
            // `ext1,default,ext2` means ext1 will happens before all of the default extensions while ext2 will after them
            ArrayList<T> extensionsResult = new ArrayList<>(activateExtensionsMap.size() + names.size());
            for (String name : names) {
                if (name.startsWith(REMOVE_VALUE_PREFIX)
                    || namesSet.contains(REMOVE_VALUE_PREFIX + name)) {
                    continue;
                }
                if (DEFAULT_KEY.equals(name)) {
                    extensionsResult.addAll(activateExtensionsMap.values());
                    continue;
                }
                if (containsExtension(name)) {
                    extensionsResult.add(getExtension(name));
                }
            }
            return extensionsResult;
        } else {
            // add extensions, will be sorted by its order
            for (String name : names) {
                if (name.startsWith(REMOVE_VALUE_PREFIX)
                    || namesSet.contains(REMOVE_VALUE_PREFIX + name)) {
                    continue;
                }
                if (DEFAULT_KEY.equals(name)) {
                    continue;
                }
                if (containsExtension(name)) {
                    activateExtensionsMap.put(getExtensionClass(name), getExtension(name));
                }
            }
            return new ArrayList<>(activateExtensionsMap.values());
        }
    }

isMatchGroup(String group, Set groups)

private boolean isMatchGroup(String group, Set<String> groups) {
        // 获取扩展点时 没传group 默认满足条件
        if (StringUtils.isEmpty(group)) {
            return true;
        }
        // Activate注解中的groups 不为空且包含传入的group时 满足条件
        if (CollectionUtils.isNotEmpty(groups)) {
            return groups.contains(group);
        }
        return false;
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/82757.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[附源码]Python计算机毕业设计SSM基于大数据的汽车流量监控(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

ESP32 ESP-IDF + LVGL + SquareLine Studio 设计 UI 界面

陈拓 2022/12/10-2022/12/12 1. 概述 关于SquareLine Studio&#xff0c;乐鑫官方的ESP技术文章有介绍&#xff1a;在 ESP 开发板上开发 UI 不再复杂 - 知乎如果您尝试过在没有图形库的情况下开发 UI&#xff0c;那么您一定知道这有多困难。幸运的是&#xff0c;由乐鑫芯片驱动…

线性代数---第六章---二次型

1二次型方的系数为主对角线上的元素 我起码要会如何根据二次型写矩阵A 2规范形是系数为1&#xff0c;-1&#xff0c;0的标准型 3二次型的正惯性指数和负惯性指数 4任一个n阶实对称阵&#xff0c;必然既相似又合同于对角阵 5用配方法化二次型为标准阵 6 正交变换得到的对角阵上…

【产品分析】从用户体验五要素分析——微信读书

本文从用户体验五要素&#xff1a;表现层面、框架层 、战略层、范围层、结构层&#xff0c;对微信读书进行了分析。 前言 互联网发展的速度也加速了知识更新的速度&#xff0c;也许今天你还手握焦点技能&#xff0c;明天就可能被遗忘在角落。一应俱全的网课、铺天盖地的资讯加…

Autosar PWM配置及使用

文章目录前言PWMPwmChannelPwmAssignedHwUnitPwmChannelIdPwmCoherentUpdatePwmDutycycleDefaultPwmIdleStatePwmNotificationPwmChannelClassPwmPeriodDefaultPwmPolarityPwmReferenceChannelPwmSafetySignalPwmShiftValuePWM输出偏移的使用PwmConfigurationOfOptApiServicesP…

Coreform Cubit (csimsoft Trelis)

Coreform Cubit (csimsoft Trelis) Coreform Cubit(csimsoft Trelis)是Coreform公司开发的最强大、最高效的工程工具之一。通常&#xff0c;公司都会启动自己的Mash项目。对于CFD分析&#xff0c;他们有FEA。这个软件可以开始你的工作&#xff0c;添加你自己的印第安形状&#…

2023年湖北建设厅七大员建筑八大员考试什么时候报名?甘建二

2023年湖北建设厅七大员建筑八大员考试什么时候报名&#xff1f; 建设厅七大员和建筑八大员有什么关系呢&#xff1f;有区别吗&#xff1f; 甘建二老师告诉你&#xff0c;建设厅七大员和建筑八大员没有区别&#xff0c;只是叫法不一样而已&#xff0c;都是一个东西。 七大员和…

Kunbernetes——二进制单节点部署

常见的k8s部署方式 1.Mini kube Minikube是一个工具&#xff0c;可以在本地快速运行一个单节点微型K8s&#xff0c;仅用于学习预览K8s的一些特性使用 部署地址: https://kubernetes.io/docs/setup/minikube 2.Kubeadmin Kubeadmin也是一个工具&#xff0c;提供kubeadm init和…

基于python的opencv中SGBM立体匹配算法实现

文章目录前言一、SGBM和SGM的区别&#xff1f;1.预处理2.代价计算3.动态规划4.后处理二、SGBM的python-opencv的实现SGBM 参数选择参考文章前言 SGBM的核心是SGM算法&#xff0c;自OpenCV2.4.6开始就已经被开源&#xff0c;非常的方便&#xff0c;并被广泛使用。 一、SGBM和SGM…

几何角度理解相机成像过程

本笔记从几何角度来理解相机的成像过程&#xff0c;我们生活在三维世界中&#xff0c;相机所捕捉到的画面是2D的&#xff0c;3D空间中的点是如何投影到2D平面的过程是本笔记关注的。 预设场景 本笔记讨论的东西基于以下场景&#xff1a; 在一个房间内放了一台相机&#xff0c;…

入职阿里的秘密武器,阿里内部“Java学习笔记”,由浅入深,通俗易懂!

导言 提起阿里&#xff0c;行外人联想到的关键词无非是“交易”、“淘宝”、“支付宝”&#xff0c;但对于程序员来说&#xff0c;阿里庞大的技术体系才是最吸引人的。实际上阿里作为国内一线互联网公司的头把交椅&#xff0c;内部的技术体系和发展都是备受关注的&#xff0c;…

Network-UART VHDL设计及仿真实现

设计内容: 实现UART及实时系统完成收发UART操作的测试系统,要求如下: 本工程包括一个测试系统,UART. UART包括baud波特率模块、UART的transfer and receive模块等 1 Objective The objective of this project is to design and build a complete UART in VHDL. Upon comp…

为什么电子商务物流对电商商家的业务很重要?

正是电子商务物流的推动推动了企业发展包括最大渠道在内的整体生态系统;店内提货、电子商务、分销商、经销商、合作伙伴和全球制造商&#xff0c;推动新客户的增长。电子商务巨头的目标是推动更多的销售并提高客户忠诚度。  无论是内部还是第三方物流公司&#xff0c;改进的电…

【小塔秉匠地图】

运行对应地图 1.先清空url运行一遍 2.填上url再次运行就可以了 对地图操作的方法 handleRowClick(row, column, event) {if (row) {this.send(camToAnchor,{name: row.ancName}) // camToAnchor:定位到锚点;ancName锚点的唯一标识} },send(funcName, options) {console.l…

优维低代码:Theme Mode 页面主题和模式

优维低代码技术专栏&#xff0c;是一个全新的、技术为主的专栏&#xff0c;由优维技术委员会成员执笔&#xff0c;基于优维7年低代码技术研发及运维成果&#xff0c;主要介绍低代码相关的技术原理及架构逻辑&#xff0c;目的是给广大运维人提供一个技术交流与学习的平台。 连载…

React 18:state概念与使用、注意问题

一、概念与基本使用 props中的所有属性都是不可变的&#xff0c;这使得React组件不能随着props的改变而改变。但在实际的开发中&#xff0c;我们更希望的是数据发生变化时&#xff0c;页面也会随着数据一起变化。React为我们提供了state用来解决这个问题。 state和props类似&…

类的赋值与浅拷贝

类的浅拷贝与深拷贝 变量的赋值操作 只是形成两个变量&#xff0c;实际上还是指向同一个对象浅拷贝 Python拷贝一般都是浅拷贝&#xff0c;拷贝时&#xff0c;对象包含的子对象内容不拷贝因此&#xff0c;源对象与拷贝对象会引用同一个子对象深拷贝 使用copy模块的deepcopy函数…

[附源码]JAVA毕业设计疫情防控期间网上教学管理(系统+LW)

[附源码]JAVA毕业设计疫情防控期间网上教学管理&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 …

window下生成SANs证书给ingress用

1.安装openssl openssl下载地址&#xff1a;http://slproweb.com/products/Win32OpenSSL.html http://slproweb.com/products/Win32OpenSSL.html 下载指定版本exe安装。 2. 修改openssl配置 为了方便我们copy一份openssl.cfg&#xff0c;用来修改 C:\Users\vamcl\Deskto…

做自媒体18个月,倒欠38万,一个自媒体创作者的自述

做自媒体18个月,倒欠38万,一个自媒体创作者的自述 疫情开始后&#xff0c;长期居家&#xff0c;工作和生活都受到了明显影响&#xff0c;偶然在网上看到了自媒体这个行业&#xff0c;号称日入几千几万。 前前后后花非不少钱买设备、买课、买会员等等&#xff0c;花费近40w颗粒…