Dubbo-spi 浅析

33 篇文章 0 订阅

Dubbo SPI

  • 扩展点:通过 SPI 机制查找并加载实现的接口-又称“扩展接口”
  • 扩展点实现:实现了扩展接口的实现类。
    JDK SPI 在查找扩展实现类的过程中,需要遍历 SPI 配置文件中定义的所有实现类,该过程中会将这些实现类全部实例化。如果 SPI 配置文件中定义了多个实现类,而我们只需要使用其中一个实现类时,就会生成不必要的对象。例如,org.apache.dubbo.rpc.Protocol 接口有 InjvmProtocol、DubboProtocol、RmiProtocol、HttpProtocol、HessianProtocol、ThriftProtocol 等多个实现,如果使用 JDK SPI,就会加载全部实现类,导致资源的浪费

Dubbo SPI 不仅解决了上述资源浪费的问题,还对 SPI 配置文件扩展和修改

首先,Dubbo 按照 SPI 配置文件的用途,将其分成了三类目录。

  • META-INF/services/ 目录:该目录下的 SPI 配置文件用来兼容 JDK SPI 。

  • META-INF/dubbo/ 目录:该目录用于存放用户自定义 SPI 配置文件。

  • META-INF/dubbo/internal/ 目录:该目录用于存放 Dubbo 内部使用的 SPI 配置文件

dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol

在这里插入图片描述
key 被称为扩展名(也就是 ExtensionName),当我们在为一个接口查找具体实现类时,可以指定扩展名来选择相应的扩展实现。例如,这里指定扩展名为 dubbo,Dubbo SPI 就知道我们要使用:org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol 这个扩展实现类,只实例化这一个扩展实现即可,无须实例化 SPI 配置文件中的其他扩展实现类。

使用 KV 格式的 SPI 配置文件的另一个好处是:让我们更容易定位到问题。假设我们使用的一个扩展实现类所在的 jar 包没有引入到项目中,那么 Dubbo SPI 在抛出异常的时候,会携带该扩展名信息,而不是简单地提示扩展实现类无法加载。

@SPI 注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SPI {

    /**
     * default extension name
     */
    String value() default "";

}
  • Protocol接口
/**
 * Protocol. (API/SPI, Singleton, ThreadSafe)
 */
@SPI("dubbo")
public interface Protocol {

在这里插入图片描述
@SPI 注解的 value 值指定了默认的扩展名称,例如,在通过 Dubbo SPI 加载 Protocol 接口实现时,如果没有明确指定扩展名,则默认会将 @SPI 注解的 value 值作为扩展名,即加载 dubbo 这个扩展名对应的 org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol 这个扩展实现类,相关的 SPI 配置文件在 dubbo-rpc-dubbo 模块中
在这里插入图片描述

ExtensionLoader 是如何处理 @SPI 注解的呢?

ExtensionLoader 位于 dubbo-common 模块中的 extension 包中,功能类似于 JDK SPI 中的 java.util.ServiceLoader。Dubbo SPI 的核心逻辑几乎都封装在 ExtensionLoader 之中(其中就包括 @SPI 注解的处理逻辑)
使用方式如下

Protocol protocol = ExtensionLoader 
   .getExtensionLoader(Protocol.class).getExtension("dubbo");

ExtensionLoader 介绍

三个核心的静态字段

  • strategies(LoadingStrategy[]类型):LoadingStrategy 接口有三个实现(通过 JDK SPI 方式加载的),如下图所示,分别对应前面介绍的三个 Dubbo SPI 配置文件所在的目录,且都继承了 Prioritized 这个优先级接口,默认优先级是

DubboInternalLoadingStrategy > DubboLoadingStrategy > ServicesLoadingStrateg

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

在这里插入图片描述

  • EXTENSION_LOADERS(ConcurrentMap<Class, ExtensionLoader>类型)
    :Dubbo 中一个扩展接口对应一个 ExtensionLoader 实例,该集合缓存了全部 ExtensionLoader 实例,其中的 Key 为扩展接口,Value 为加载其扩展实现的 ExtensionLoader 实例。

  • EXTENSION_INSTANCES(ConcurrentMap<Class<?>, Object>类型):该集合缓存了扩展实现类与其实例对象的映射关系。在前文示例中,Key 为 Class,Value 为 DubboProtocol 对象。

ExtensionLoader 实例字段
  • type(Class<?>类型):当前 ExtensionLoader 实例负责加载扩展接口。

  • cachedDefaultName(String类型):记录了 type 这个扩展接口上 @SPI 注解的 value 值,也就是默认扩展名。

  • cachedNames(ConcurrentMap<Class<?>, String>类型):缓存了该 ExtensionLoader 加载的扩展实现类与扩展名之间的映射关系。

  • cachedClasses(Holder<Map<String, Class<?>>>类型):缓存了该 ExtensionLoader 加载的扩展名与扩展实现类之间的映射关系。cachedNames 集合的反向关系缓存。

  • cachedInstances(ConcurrentMap<String, Holder>类型):缓存了该 ExtensionLoader 加载的扩展名与扩展实现对象之间的映射关系。

getExtensionLoader方法
    /**
     * 一个扩展接口对应一个 ExtensionLoader 实例,
     * 该集合缓存了全部 ExtensionLoader 实例,
     * 其中的 Key 为扩展接口,Value 为加载其扩展实现的 ExtensionLoader 实例。
     */
    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);
    /**
     * 该集合缓存了扩展实现类与其实例对象的映射关系。在前文示例中,Key 为 Class,Value 为 DubboProtocol 对象。
     */
    private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64);

   /**
     * 根据扩展接口从 EXTENSION_LOADERS 缓存中查找相应的 ExtensionLoader 实例
     */
    @SuppressWarnings("unchecked")
    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        //判断是否为NULL
        if (type == null) {
            throw new IllegalArgumentException("Extension type == null");
        }
        //不是接口就报错
        if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
        }
        //判断 是否有扩展注解SPI,没有就报错  type.isAnnotationPresent(SPI.class);
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type (" + type +
                    ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
        }
        /**
         * 从集合中获取对应的接口的ExtensionLoader
         */
        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        if (loader == null) {
            //如果为空就生成一个新的,并放入集合中
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        //返回ExtensionLoader
        return loader;
    }

getOrCreateHolder

 /**
     * 缓存了该 ExtensionLoader 加载的扩展名与扩展实现对象之间的映射关系。
     */
    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
   private Holder<Object> getOrCreateHolder(String name) {
        //根据名称获取实现
        Holder<Object> holder = cachedInstances.get(name);
        if (holder == null) {
            //为空就创建一个放入缓存中
            cachedInstances.putIfAbsent(name, new Holder<>());
            //再获取出来
            holder = cachedInstances.get(name);
        }
        return holder;
    }
getExtension方法
    /**
     * 一个扩展接口对应一个 ExtensionLoader 实例,
     * 该集合缓存了全部 ExtensionLoader 实例,
     * 其中的 Key 为扩展接口,Value 为加载其扩展实现的 ExtensionLoader 实例。
     */
    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);
    /**
     * 该集合缓存了扩展实现类与其实例对象的映射关系。在前文示例中,Key 为 Class,Value 为 DubboProtocol 对象。
     */
    private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64);
    /**
     *
     * 缓存了该 ExtensionLoader 加载的扩展实现类与扩展名之间的映射关系。
     */
    private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
    /**
     * 缓存了该 ExtensionLoader 加载的扩展名与扩展实现类之间的映射关系。cachedNames 集合的反向关系缓存
     */
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();

    private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>();
    /**
     * 缓存了该 ExtensionLoader 加载的扩展名与扩展实现对象之间的映射关系。
     */
    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();

    /**
     * Find the extension with the given name. If the specified name is not found, then {@link IllegalStateException}
     * will be thrown.
     * 得到接口对应的 ExtensionLoader 对象之后会调用其 getExtension() 方法,根据传入的扩展名称从 cachedInstances 缓存中查找扩展实现的实例
     */
    @SuppressWarnings("unchecked")
    public T getExtension(String name) {
        if (StringUtils.isEmpty(name)) {
            //名称为空
            throw new IllegalArgumentException("Extension name == null");
        }
        if ("true".equals(name)) {
            //加载默认的扩展实现 如果name=true
            return getDefaultExtension();
        }
        //getOrCreateHolder()方法中封装了查找cachedInstances缓存的逻辑
        final Holder<Object> holder = getOrCreateHolder(name);
        Object instance = holder.get();
        //double-check防止并发问题
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    //根据扩展名从SPI配置文件中查找对应的扩展实现类 见下面
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }


  • 获取 cachedClasses 缓存,根据扩展名从 cachedClasses 缓存中获取扩展实现类。如果 cachedClasses 未初始化,则会扫描前面介绍的三个 SPI 目录获取查找相应的 SPI 配置文件,然后加载其中的扩展实现类,最后将扩展名和扩展实现类的映射关系记录到 cachedClasses 缓存中。这部分逻辑在 loadExtensionClasses() 和 loadDirectory() 方法中。

  • 根据扩展实现类从 EXTENSION_INSTANCES 缓存中查找相应的实例。如果查找失败,会通过反射创建扩展实现对象。

  • 自动装配扩展实现对象中的属性(即调用其 setter)。这里涉及 ExtensionFactory 以及自动装配的相关内容,本课时后面会进行详细介绍。

  • 自动包装扩展实现对象。这里涉及 Wrapper 类以及自动包装特性的相关内容

  • 如果扩展实现类实现了 Lifecycle 接口,在 initExtension() 方法中会调用 initialize() 方法进行初始化。

getExtensionClasses
  private Map<String, Class<?>> getExtensionClasses() {
        //从缓存中获取 ExtensionLoader 加载的扩展名与扩展实现类之间的映射关系。cachedNames 集合的反向关系缓存
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    //加载配置文件
                    classes = loadExtensionClasses();
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }
getExtensionClasses

    private Map<String, Class<?>> getExtensionClasses() {
        //从缓存中获取 ExtensionLoader 加载的扩展名与扩展实现类之间的映射关系。cachedNames 集合的反向关系缓存
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    //加载配置文件
                    classes = loadExtensionClasses();
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }
loadExtensionClasses 加载扩展Classes

    /**
     * synchronized in getExtensionClasses 同步加载
     */
    private Map<String, Class<?>> loadExtensionClasses() {
        //获取默认的扩展实现名称
        cacheDefaultExtensionName();
        //创建一个窗口
        Map<String, Class<?>> extensionClasses = new HashMap<>();
        /**
         * META-INF/dubbo/
         * META-INF/dubbo/internal/
         * META-INF/services/
         * META-INF/dubbo/external/
         * 根据不同的加载策略去不同的文件夹下加载扩展
         */
        for (LoadingStrategy strategy : strategies) {
            loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
            loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
        }

        return extensionClasses;
    }

@Adaptive 注解与适配器

@Adaptive 注解用来实现 Dubbo 的适配器功能,那什么是适配器呢?
Dubbo 中的 ExtensionFactory 接口有三个实现类,如下图所示,ExtensionFactory 接口上有 @SPI 注解,AdaptiveExtensionFactory 实现类上有 @Adaptive 注解。
在这里插入图片描述


@SPI
public interface ExtensionFactory {

    /**
     * Get extension.
     *
     * @param type object type.
     * @param name object name.
     * @return object instance.
     */
    <T> T getExtension(Class<T> type, String name);

}

在这里插入图片描述

AdaptiveExtensionFactory 有Adaptive注解,其它2个接口没有


@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {

AdaptiveExtensionFactory 不实现任何具体的功能,而是用来适配 ExtensionFactory 的 SpiExtensionFactory 和 SpringExtensionFactory 这两种实现。AdaptiveExtensionFactory 会根据运行时的一些状态来选择具体调用 ExtensionFactory 的哪个实现。

@Adaptive 注解还可以加到接口方法之上,Dubbo 会动态生成适配器类。例如,Transporter接口有两个被 @Adaptive 注解修饰的方法:


@SPI("netty")
public interface Transporter {


    @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
    RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException;

    @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
    Client connect(URL url, ChannelHandler handler) throws RemotingException;

}

Dubbo 会生成一个 Transporter$Adaptive 适配器类,该类继承了 Transporter 接口:


public class Transporter$Adaptive implements Transporter { 
    public org.apache.dubbo.remoting.Client connect(URL arg0, ChannelHandler arg1) throws RemotingException { 
        // 必须传递URL参数 
        if (arg0 == null) throw new IllegalArgumentException("url == null"); 
        URL url = arg0; 
        // 确定扩展名,优先从URL中的client参数获取,其次是transporter参数 
        // 这两个参数名称由@Adaptive注解指定,最后是@SPI注解中的默认值 
        String extName = url.getParameter("client",
            url.getParameter("transporter", "netty")); 
        if (extName == null) 
            throw new IllegalStateException("..."); 
        // 通过ExtensionLoader加载Transporter接口的指定扩展实现 
        Transporter extension = (Transporter) ExtensionLoader 
              .getExtensionLoader(Transporter.class) 
                    .getExtension(extName); 
        return extension.connect(arg0, arg1); 
    } 
    ... 
}

生成 Transporter$Adaptive 这个类的逻辑位于 ExtensionLoader.createAdaptiveExtensionClass() 方法

确定了 @Adaptive 注解的作用之后,我们回到 ExtensionLoader.createExtension() 方法,其中在扫描 SPI 配置文件的时候,会调用 loadClass() 方法加载 SPI 配置文件中指定的类

在这里插入图片描述
loadClass() 方法中会识别加载扩展实现类上的 @Adaptive 注解,将该扩展实现的类型缓存到 cachedAdaptiveClass 这个实例字段上(volatile修饰)



private void loadClass(){ 
    if (clazz.isAnnotationPresent(Adaptive.class)) { 
        // 缓存到cachedAdaptiveClass字段 
        cacheAdaptiveClass(clazz, overridden);
    } else ... 
}

通过 ExtensionLoader.getAdaptiveExtension() 方法获取适配器实例,并将该实例缓存到 cachedAdaptiveInstance 字段(Holder类型)中,核心流程如下:

  • 首先,检查 cachedAdaptiveInstance 字段中是否已缓存了适配器实例,如果已缓存,则直接返回该实例即可。

  • 然后,调用 getExtensionClasses() 方法,其中就会触发前文介绍的 loadClass() 方法,完成 cachedAdaptiveClass 字段的填充。

  • 如果存在 @Adaptive 注解修饰的扩展实现类,该类就是适配器类,通过 newInstance() 将其实例化即可。如果不存在 @Adaptive 注解修饰的扩展实现类,就需要通过 createAdaptiveExtensionClass() 方法扫描扩展接口中方法上的 @Adaptive 注解,动态生成适配器类,然后实例化。

  • 接下来,调用 injectExtension() 方法进行自动装配,就能得到一个完整的适配器实例。

  • 最后,将适配器实例缓存到 cachedAdaptiveInstance 字段,然后返回适配器实例。

注入

在这里插入图片描述
ubbo SPI 在拿到扩展实现类的对象(以及 Wrapper 类的对象)之后,还会调用 injectExtension() 方法扫描其全部 setter 方法,并根据 setter 方法的名称以及参数的类型,加载相应的扩展实现,然后调用相应的 setter 方法填充属性,这就实现了 Dubbo SPI 的自动装配特性。简单来说,自动装配属性就是在加载一个扩展点的时候,将其依赖的扩展点一并加载,并进行装配。

injectExtension方法
    private T injectExtension(T instance) {
        //检测objectFactory字段
        if (objectFactory == null) {
            return instance;
        }

        try {
            for (Method method : instance.getClass().getMethods()) {
                //方法遍历判断是否为setter方法  如果不是,忽略该方法继续下一个方法
                if (!isSetter(method)) {
                    continue;
                }
                /**
                 * Check {@link DisableInject} to see if we need auto injection for this property
                 */
                if (method.getAnnotation(DisableInject.class) != null) {
                    //判断是否禁止注入 如果方法上明确标注了@DisableInject注解,忽略该方法
                    continue;
                }
                //根据setter方法的参数,确定扩展接口
                Class<?> pt = method.getParameterTypes()[0];
                //判断是否为基本类型 原始类型(boolean、char、byte、short、int、long、float、double)
                //如果参数为简单类型,忽略该setter方法(略)
                if (ReflectUtils.isPrimitives(pt)) {
                    continue;
                }

                try {
                    //setter方法注入
                    String property = getSetterProperty(method);
                    //从扩展中获取对象 加载并实例化扩展实现类 
                    Object object = objectFactory.getExtension(pt, property);
                    if (object != null) {
                        method.invoke(instance, object);
                    }
                } 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;
    }

injectExtension() 方法实现的自动装配依赖了 ExtensionFactory(即 objectFactory 字段),前面我们提到过 ExtensionFactory 有 SpringExtensionFactory 和 SpiExtensionFactory 两个真正的实现(还有一个实现是 AdaptiveExtensionFactory 是适配器)

SpiExtensionFactory
public class SpiExtensionFactory implements ExtensionFactory {

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        //判断是否为接口,是否有SPI注解
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            // 查找type对应的ExtensionLoader实例
            ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
            if (!loader.getSupportedExtensions().isEmpty()) {
                //获取适配器实现 
                return loader.getAdaptiveExtension();
            }
        }
        return null;
    }

}
SpringExtensionFactory

将属性名称作为 Spring Bean 的名称,从 Spring 容器中获取 Bean:


/**
 * SpringExtensionFactory
 */
public class SpringExtensionFactory implements ExtensionFactory {
    @Override
    @SuppressWarnings("unchecked")
    public <T> T getExtension(Class<T> type, String name) {

        //SPI should be get from SpiExtensionFactory
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            //如果是接口,并且有SPI注解,就使用SpiExtensionFactory获取
            return null;
        }

        for (ApplicationContext context : CONTEXTS) {
            //从Spring BeanFactory中获取对应的Bean实现
            T bean = BeanFactoryUtils.getOptionalBean(context, name, type);
            if (bean != null) {
                return bean;
            }
        }

        logger.warn("No spring extension (bean) named:" + name + ", try to find an extension (bean) of type " + type.getName());

        return null;
    }
Activate注解与自动激活特性

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

  • group 属性:修饰的实现类是在 Provider 端被激活还是在 Consumer 端被激活。

  • value 属性:修饰的实现类只在 URL 参数中出现指定的 key 时才会被激活。

  • order 属性:用来确定扩展实现类的排序。
    loadClass() 方法对 @Activate 的扫描,其中会将包含 @Activate 注解的实现类缓存到 cachedActivates 这个实例字段(Map<String, Object>类型,Key为扩展名,Value为 @Activate 注解):

private void loadClass(){ 
    if (clazz.isAnnotationPresent(Adaptive.class)) { 
        // 处理@Adaptive注解 
        cacheAdaptiveClass(clazz, overridden); 
    } else if (isWrapperClass(clazz)) { // 处理Wrapper类 
        cacheWrapperClass(clazz); 
    } else { // 处理真正的扩展实现类 
        clazz.getConstructor(); // 扩展实现类必须有无参构造函数 
        ...// 兜底:SPI配置文件中未指定扩展名称,则用类的简单名称作为扩展名(略) 
        String[] names = NAME_SEPARATOR.split(name); 
        if (ArrayUtils.isNotEmpty(names)) { 
            // 将包含@Activate注解的实现类缓存到cachedActivates集合中 
            cacheActivateClass(clazz, names[0]); 
            for (String n : names) { 
                // 在cachedNames集合中缓存实现类->扩展名的映射 
                cacheName(clazz, n);
                // 在cachedClasses集合中缓存扩展名->实现类的映射 
                saveInExtensionClass(extensionClasses, clazz, n, 
                     overridden); 
            } 
        } 
    } 
}

使用 cachedActivates 这个集合的地方是 getActivateExtension() 方法。首先来关注 getActivateExtension() 方法的参数:url 中包含了配置信息,values 是配置中指定的扩展名,group 为 Provider 或 Consumer


public interface CommonConstants {

    String PROVIDER = "provider";

    String CONSUMER = "consumer";
    ...
  • 首先,获取默认激活的扩展集合。默认激活的扩展实现类有几个条件:①在 cachedActivates 集合中存在;②@Activate 注解指定的 group 属性与当前 group 匹配;③扩展名没有出现在 values 中(即未在配置中明确指定,也未在配置中明确指定删除);④URL 中出现了 @Activate 注解中指定的 Key。

  • 然后,按照 @Activate 注解中的 order 属性对默认激活的扩展集合进行排序。

  • 最后,按序添加自定义扩展实现类的对象。


public List<T> getActivateExtension(URL url, String[] values, 
         String group) { 
    List<T> activateExtensions = new ArrayList<>(); 
    // values配置就是扩展名 
    List<String> names = values == null ?
            new ArrayList<>(0) : asList(values); 
    if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {// ---1 
        getExtensionClasses(); // 触发cachedActivates等缓存字段的加载 
        for (Map.Entry<String, Object> entry :
                  cachedActivates.entrySet()) { 
            String name = entry.getKey(); // 扩展名 
            Object activate = entry.getValue(); // @Activate注解 
            String[] activateGroup, activateValue; 
            if (activate instanceof Activate) { // @Activate注解中的配置 
                activateGroup = ((Activate) activate).group(); 
                activateValue = ((Activate) activate).value(); 
            } else { 
                continue; 
            } 
            if (isMatchGroup(group, activateGroup) // 匹配group 
                    // 没有出现在values配置中的,即为默认激活的扩展实现 
                    && !names.contains(name)
                    // 通过"-"明确指定不激活该扩展实现 
                    && !names.contains(REMOVE_VALUE_PREFIX + name)
                    // 检测URL中是否出现了指定的Key 
                    && isActive(activateValue, url)) { 
                // 加载扩展实现的实例对象,这些都是激活的 
                activateExtensions.add(getExtension(name)); 
            } 
        } 
        // 排序 --- 2 
        activateExtensions.sort(ActivateComparator.COMPARATOR); 
    } 

    List<T> loadedExtensions = new ArrayList<>(); 
    for (int i = 0; i < names.size(); i++) { // ---3 
        String name = names.get(i); 
        // 通过"-"开头的配置明确指定不激活的扩展实现,直接就忽略了 
        if (!name.startsWith(REMOVE_VALUE_PREFIX) 
                && !names.contains(REMOVE_VALUE_PREFIX + name)) { 
            if (DEFAULT_KEY.equals(name)) { 
                if (!loadedExtensions.isEmpty()) { 
                    // 按照顺序,将自定义的扩展添加到默认扩展集合前面 
                    activateExtensions.addAll(0, loadedExtensions); 
                    loadedExtensions.clear(); 
                } 
            } else { 
                loadedExtensions.add(getExtension(name)); 
            } 
        } 
    } 
    if (!loadedExtensions.isEmpty()) { 
        // 按照顺序,将自定义的扩展添加到默认扩展集合后面 
        activateExtensions.addAll(loadedExtensions); 
    } 
    return activateExtensions; 
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Dubbo-Admin 是 Dubbo 提供的一个可视化管理平台,用于监控和管理 Dubbo 服务。安装和部署 Dubbo-Admin 需要以下步骤: 1. 下载 Dubbo-Admin 的安装包,可以从 Dubbo 的官方 GitHub 仓库中获取。你可以访问 https://github.com/apache/dubbo-admin/releases 下载最新版本的安装包。 2. 解压下载的安装包到你选择的目录。你可以使用以下命令解压: ```shell unzip dubbo-admin-x.x.x.zip ``` 3. 进入解压后的目录,找到 `dubbo-admin-server` 目录。 4. 打开 `dubbo-admin-server` 目录下的 `src/main/resources/application.properties` 文件,修改其中的配置信息。 - 配置 Dubbo 注册中心的地址:`dubbo.registry.address=zookeeper://localhost:2181`,根据你实际的注册中心地址进行修改。 - 配置 Dubbo Admin 的访问端口:`server.port=8080`,根据你的需求进行修改。 5. 保存并关闭 `application.properties` 文件。 6. 在 `dubbo-admin-server` 目录下执行以下命令来构建 Dubbo-Admin: ```shell mvn clean package ``` 7. 构建完成后,进入 `target` 目录,执行以下命令来启动 Dubbo-Admin: ```shell java -jar dubbo-admin-x.x.x.jar ``` 8. 等待启动完成,访问 http://localhost:8080 即可进入 Dubbo-Admin 的管理界面。 请注意,安装和部署 Dubbo-Admin 需要满足以下依赖条件: - JDK 1.8 或以上版本 - Maven 3.x - ZooKeeper 3.x 或以上版本 希望以上步骤对你有帮助!如果还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值