我是靠谱客的博主 野性跳跳糖,最近开发中收集的这篇文章主要介绍dubbo 扩展点加载机制,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

概述

扩展点加载机制在dubbo框架中无处不在, 它也是dubbo 高扩展性的关键实现. 所以要彻底理解dubbo的实现原理, 弄明白扩展点加载机制至关重要.

dubbo 的扩展点机制本质上是在Java SPI 基础 上改造的. 那么什么是Java 的SPI 呢

所有技术的衍生都是基于业务需要,因为有需要,才会想解决办法, 进而产生各种技术. 那么Java的SPI 的产生又是基于什么样的需求呢?
我们知道JDBC是为了统一数据库的操作而由Sun 公司定制的一套接口标准, 而具体的实现则由各个数据库厂商提供, 比如我们使用mysql数据库,那么我们就要引用mysql的依赖, 这个依赖就是对JDBC的实现. 那么Java 虚拟机怎么知道接口的实现是哪个呢, 就是通过Java的SPI 机制

SPI机制的核心类是ServiceLoader, 通过调用ServiceLoader.load(接口.class) 方法加载该接口的所有实现.
load 方法的实现原理是 去 METSA_INF/services 目录寻找文件名为该接口名的文件, 然后逐行加载该文件中写的接口的实现类

使用JDBC的例子来理解就是 Java 定制了一套数据库接口, 然后数据库厂商提供实现类,然后在他们项目的 META-INF/services包 创建文件名为接口全类名的文件, 文件内容写他对这个接口的实现类的全类名.

比如我们引入了mysql的依赖, Java虚拟机通过这个规则就会加载到对应的实现了.

我们可以看到使用这种加载机制, 可以很好的将接口和实现分离, 极大的提高了灵活性和扩展性.

dubbo 扩展点加载机制在此基础上做了扩展和改进

  1. dubbo 加载的路径包括 META-INF/services, META-INF/dubbo 和 META-INF/dubbo/internal
  2. dubbo 可以按扩展名来使用不用的扩展类, Java SPI 是将实现类的全类名直接写在文件中, 而 dubbo 的形式是 扩展名=扩展实现全类名,因此可以按扩展名去使用对应的扩展类
  3. dubbo 是按需实例化的, 使用Java的ServiceLoader.load 方法加载完实现类,它会对所有的实现类都实例化. 而dubbo 是根据你要使用的那个扩展名来实例化对应的实现的, 其他的扩展类放在缓存中
  4. dubbo 增加实现了 类似AOP和IOC的功能

三个关键注解

dubbo 扩展点加载机制有三个重要的注解,分别是

  1. @SPI: 主要是标记功能, 标记一个接口是Dubbo SPI 接口, 也就是表明它是一个扩展点, 可以有多个不同的实现
  2. @Adaptive: 标注在类上, 表示这个类是该扩展点的默认实现, 最多只能有一个扩展点实现使用这个注解, 也就是一个扩展点的自适应实现智能有一个; 标注在方法上, dubbo 框架会为这个扩展点生成一个默认的自适应实现.
  3. @Activate: 主要的使用场景是 在不同的场景中需要激活不同的扩展点实现

ExtensionLoader的实现原理

ExtensionLoader 的构造方法是私有的, 使用方法是通过调用静态方法来获取一个ExtensionLoader实例,然后调用getExtension(String name) 方法来加载以name为扩展名的实现
即常见用法是

ExtensionLoader.getExtensionLoader(扩展点.class).getExtension(扩展名)

getExtensionLoader 方法首先会检查传入的类型是否为一个合法的扩展点,也就是是否是一个有@SPI 注解的接口
然后从 EXTENSION_LOADERS 缓存中获取该type对应的扩展点加载器,如果缓存中没有再新建一个, 为什么要从缓存中获取扩展点加载器呢?
因为dubbo 对扩展点的实现设置了很多缓存, 比如扩展点的实现类,自适应类,包装类等等, 这些缓存就保存在这个扩展点对应的扩展点加载器中.

可以看到在构造方法中 objectFactory 也是通过加载扩展点实现来赋值的, objectFactory 具体在给扩展点注入其他扩展点属性的时候会使用到, ExtensionFactory 在后面再详细解析

// 静态的,全局唯一
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        if (type == null) {
            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) {
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        return loader;
    }

// 私有的静态方法
private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory =
                (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

ExtensionLoader 有三个获取扩展点实现的方法, 分别是 getExtension, getAdaptiveExtension 和getActivateExtension 分别获取普通扩展点实现, 自适应扩展点实现, 自动激活的扩展点实现

普通扩展类: 除了自适应扩展类, 包装扩展类,其他的就是普通扩展类
自适应扩展类: 有@Adaptive注解的扩展类, 或者扩展点接口中的方法有@Adaptive注解的, 此时 dubbo 框架会动态生成一个
自动激活扩展点: 主要使用在有多个扩展实现, 需要根据不同条件被激活的场景, 比如Filter需要多个同时被激活.

getExtension

参数说明:
name: 扩展名
wrap: 是否对扩展实例进行包装, 默认为真, 封装后面会详细解析
总体流程:
1. 如果name 为"true" 则使用默认的实现,后面会解析
2. 先从cachedInstances 普通扩展实例缓存中获取, 如果没有先new一个Holder占个坑
3. 然后使用double-check 机制对holder加锁, 然后调用 createExtension 创建实例,在放到holder中
这里有个小问题,为什么要使用holder,并且加锁呢? cachedInstances 已经使用了 ConcurrentHashMap,可以保证线程安全啊?
其中一个原因, 是因为要获取一个扩展点的实例, 需要加载这个扩展点的所有实现类, 实例化之后还要对实例进行依赖注入和包装, 最后才是一个完整的实例,如果直接在实例化完成后放入ConcurrentHashMap, 由 ConcurrentHashMap 保证只有一个 实例, 那么可能会导致多个线程都执行了实例化的过程, 而最后只有一个线程的结果被采用, 造成浪费

public T getExtension(String name) {
        return getExtension(name, true);
    }

    public T getExtension(String name, boolean wrap) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        final Holder<Object> holder = getOrCreateHolder(name);
        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;
    }

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

// Holder 实现
public class Holder<T> {
// volatile 保证了线程间可见
    private volatile T value;

    public void set(T value) {
        this.value = value;
    }

    public T get() {
        return value;
    }

}

createExtension

总体流程:
1. 通过 getExtensionClasses 方法获取该type类型的扩展点的所有实现类, 然后拿到扩展名为name的实现类
2. 从EXTENSION_INSTANCES 缓冲中获取该扩展类的实例,如果没有则新构造一个
3. 调用 injectExtension(instance) 方法对该实例进行属性注入
4. 如果要进行包装, 则遍历排序后的 cachedWrapperClasses (在getExtensionClasses过程中会进行缓存), 用 instance 去实例化包装类,然后对包装类的实例调用 injectExtension 方法进行属性注入之后, 作为新的 instance. 我们看到如果有多个包装类会进行层层包装
5. 调用initExtension(instance) 方法


// 静态的,全局唯一, 一个扩展点实现类只会保存一个实例
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64);

 private T createExtension(String name, boolean wrap) {
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null || unacceptableExceptions.contains(name)) {
            throw findException(name);
        }
        try {
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.getDeclaredConstructor().newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            injectExtension(instance);


            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);
                        if (wrapper == null
                                || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
                            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                        }
                    }
                }
            }

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

getExtensionClasses

一样的套路,先尝试从缓存中获取, 如果没有再通过 loadExtensionClasses 加载
这里同样是锁着占位坑 holder


private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();

private Map<String, Class<?>> getExtensionClasses() {
        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

总体逻辑:
1. 使用cacheDefaultExtensionName 方法将扩展点 @SPI注解中的value 作为 默认扩展名缓存在 cachedDefaultName 中, 在上述的name 为 “true” 时返回默认扩展实现中会使用到
2. 遍历所有的加载策略, 通过 loadDirectory 方法加载各个加载策略的路径下的该扩展点的所有实现, 这里调用两次 loadDirectory 方法是为了兼容dubbo 贡献给apache之前的实现

加载策略 strategies 也是通过java SPI 来实现的, dubbo 默认提供的LoadingStrategy 一共有三种, 分别对应三个加载路径
org.apache.dubbo.common.extension.DubboInternalLoadingStrategy ----> META-INF/dubbo/internal
org.apache.dubbo.common.extension.DubboLoadingStrategy ------> META-INF/dubbo
org.apache.dubbo.common.extension.ServicesLoadingStrategy -------> META-INF/services

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

        Map<String, Class<?>> extensionClasses = new HashMap<>();

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


private static final Pattern NAME_SEPARATOR = Pattern.compile("\s*[,]+\s*");

private void cacheDefaultExtensionName() {
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation == null) {
            return;
        }

        String value = defaultAnnotation.value();
        if ((value = value.trim()).length() > 0) {
            String[] names = NAME_SEPARATOR.split(value);
            if (names.length > 1) {
                throw new IllegalStateException("More than 1 default extension name on extension " + type.getName()
                        + ": " + Arrays.toString(names));
            }
            if (names.length == 1) {
                cachedDefaultName = names[0];
            }
        }
    }


private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();

private static LoadingStrategy[] loadLoadingStrategies() {
        return stream(ServiceLoader.load(LoadingStrategy.class).spliterator(), false)
                .sorted()
                .toArray(LoadingStrategy[]::new);
    }
    
loadDirectory

总体逻辑:
1. 调用 类加载器的 getResources 方法获取所有的对应路径下名为扩展点全类名的文件
2. 调用 loadResource 方法解析每个文件

 private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type,
                               boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) {
        String fileName = dir + type;
        try {
            Enumeration<java.net.URL> urls = null;
            ClassLoader classLoader = findClassLoader();

            // try to load from ExtensionLoader's ClassLoader first
            if (extensionLoaderClassLoaderFirst) {
                ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
                if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
                    urls = extensionLoaderClassLoader.getResources(fileName);
                }
            }

            if (urls == null || !urls.hasMoreElements()) {
                if (classLoader != null) {
                    urls = classLoader.getResources(fileName);
                } else {
                    urls = ClassLoader.getSystemResources(fileName);
                }
            }

            if (urls != null) {
                while (urls.hasMoreElements()) {
                    java.net.URL resourceURL = urls.nextElement();
                    loadResource(extensionClasses, classLoader, resourceURL, overridden, excludedPackages);
                }
            }
        } catch (Throwable t) {
            logger.error("Exception occurred when loading extension class (interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }
loadResource

总体逻辑:

  1. 逐行解析文件, 截取 注释 # 前面的部分,使用 = 分隔,分别获取扩展名和实现类名
  2. 调用 Class.forName(clazz, true, classLoader) 加载实现类
  3. 调用 loadClass 进行分类缓存
  private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader,
                              java.net.URL resourceURL, boolean overridden, String... excludedPackages) {
        try {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
                String line;
                String clazz = null;
                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) {
                        try {
                            String name = null;
                            int i = line.indexOf('=');
                            if (i > 0) {
                            // 扩展名
                                name = line.substring(0, i).trim();
                                // 扩展类全路径
                                clazz = line.substring(i + 1).trim();
                            } else {
                            // 兼容 Java SPI
                                clazz = line;
                            }
                            if (StringUtils.isNotEmpty(clazz) && !isExcluded(clazz, excludedPackages)) {
                                loadClass(extensionClasses, resourceURL, Class.forName(clazz, true, classLoader), name, overridden);
                            }
                        } 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);
        }
    }

总体逻辑:

  1. 如果该扩展类有@Adaptive注解, 将它缓存在 cachedAdaptiveClass
  2. 如果该扩展类是包装类(即实现了type接口,又将type接口作为构造器参数), 将它缓存在cachedWrapperClasses 中, 在上述提到的包装过程中会使用
  3. 其他的就是普通的扩展类,缓存在cachedClasses 中, 对于普通扩展类,还会调用一个方法 cacheActivateClass 会将扩展名和Activate 注解缓存在 cachedActivates ,在 getActivateExtension 中会使用
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.");
        }
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            cacheAdaptiveClass(clazz, overridden);
        } else if (isWrapperClass(clazz)) {
            cacheWrapperClass(clazz);
        } else {
            clazz.getConstructor();
            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)) {
                cacheActivateClass(clazz, names[0]);
                for (String n : names) {
                    cacheName(clazz, n);
                    saveInExtensionClass(extensionClasses, clazz, n, overridden);
                }
            }
        }
    }

private void cacheActivateClass(Class<?> clazz, String name) {
        Activate activate = clazz.getAnnotation(Activate.class);
        if (activate != null) {
            cachedActivates.put(name, activate);
        } else {
            // support com.alibaba.dubbo.common.extension.Activate
            com.alibaba.dubbo.common.extension.Activate oldActivate =
                    clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.class);
            if (oldActivate != null) {
                cachedActivates.put(name, oldActivate);
            }
        }
    }

injectExtension

总体逻辑:

  1. 如果该扩展点加载器没有objectFactory,则不进行属性注入, 通过构造函数,我们知道只有 ExtensionFactory 会不进行属性注入
  2. 遍历该实例的所有setter方法, 而且是没有禁止注入注解DisableInject 的方法, 对于基本类型也不进行注入,
  3. 通过截取setter方法获得属性名, 然后去ExtensionFactory 扩展点工厂找实例, 然后注入
private T injectExtension(T instance) {

        if (objectFactory == null) {
            return instance;
        }

        try {
            for (Method method : instance.getClass().getMethods()) {
                if (!isSetter(method)) {
                    continue;
                }
                /**
                 * Check {@link DisableInject} to see if we need auto injection for this property
                 */
                if (method.getAnnotation(DisableInject.class) != null) {
                    continue;
                }
                Class<?> pt = method.getParameterTypes()[0];
                if (ReflectUtils.isPrimitives(pt)) {
                    continue;
                }

                try {
                    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;
    }
private String getSetterProperty(Method method) {
        return method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
    }

getAdaptiveExtension

一样的套路,先尝试从缓冲中获取自适应实例,如果没有,则占坑,加锁, 调用 createAdaptiveExtension 创建自适应实例


private final Holder<Object> cachedAdaptiveInstance = new Holder<>();

 public T getAdaptiveExtension() {
        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;
    }

createAdaptiveExtension, getAdaptiveExtensionClass

  1. createAdaptiveExtension 方法
    调用getAdaptiveExtensionClass 获取自适应扩展类, 进行实例化后,在进行属性注入
    这里我们看到对于自适应扩展实例不会进行包装
  2. getAdaptiveExtensionClass 方法
  • 先调用getExtensionClasses方法,通过上面我们知道该方法会加载type扩展点的所有类型,并进行分类缓存, 如果有@Adaptive 注解的实现类,则会缓存在 cachedAdaptiveClass
  • 调用createAdaptiveExtensionClass 直接生成自适应类的字符串形式的代码,然后使用动态编译器编译 得到自适应扩展类
  • AdaptiveClassCodeGenerator 生成代码的规则如下
    1. 像我们编写一般的Java类一样,首先写包名,import, 类名,该类名为扩展点类名加$Adaptive后缀, 实现的类,继承的类等
    2. 类的内容是逐一生成扩展点的方法实现, 对于没有@Adaptive注解的方法, 生成空实现, 对于有@Adaptive注解的方法,在生成的方法中会调用

ExtensionLoader.getExtensionLoader(扩展点).getExtension(extName)

那么extName怎么获取呢, 在URL中找 @Adaptive 中声明的key , 如果有拿value作为扩展名,如果 @Adaptive 没有声明key, 那么用. 分隔驼峰类名得到extName, 如果没有对应的扩展类实现, 则使用默认扩展名
获取扩展点实现后,会调用该扩展点的该接口方法.
比如对于Transporter,会生成Transporter$Adaptive 类, 生成的 bind 方法会 获取扩展点实现后,调用bind 方法

 private T createAdaptiveExtension() {
        try {
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }
private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

private Class<?> createAdaptiveExtensionClass() {
        String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
        ClassLoader classLoader = findClassLoader();
        org.apache.dubbo.common.compiler.Compiler compiler =
                ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        return compiler.compile(code, classLoader);
    }
@SPI("netty")
public interface Transporter {

    /**
     * Bind a server.
     *
     * @param url     server url
     * @param handler
     * @return server
     * @throws RemotingException
     * @see org.apache.dubbo.remoting.Transporters#bind(URL, ChannelHandler...)
     */
    @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
    RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException;

    /**
     * Connect to a server.
     *
     * @param url     server url
     * @param handler
     * @return client
     * @throws RemotingException
     * @see org.apache.dubbo.remoting.Transporters#connect(URL, ChannelHandler...)
     */
    @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
    Client connect(URL url, ChannelHandler handler) throws RemotingException;

}

getActivateExtension

  1. 先从URL中获取key 对应的值,如果有多个则用, 分隔,作为names
  2. 如果names中不包含-default
  • 调用 getExtensionClasses, 通过上面我们知道这个函数去加载type 扩展点的所有实现, 在加载完之后进行分类缓存时,对于普通扩展类,如果有@Activate 注解, 会将扩展名和Activate 注解缓存在 cachedActivates , 在此时则需要使用该缓存
  • 遍历 cachedActivates , 如果names中不包含name, 也不包含-name, 并且@Activate中group和values能和group 和URL中的参数匹配,则激活该扩展名

这个方法的逻辑比较绕, 关键是我们弄清楚要激活哪些扩展类即可

  1. 先从URL中获取key 对应的值,如果有多个则用, 分隔,作为names
  2. 激活分为两部分,一部分是从缓存 cachedActivates 中获取. 通过上面我们知道getExtensionClasses这个函数去加载type 扩展点的所有实现, 在加载完之后进行分类缓存时,对于普通扩展类,如果有@Activate 注解, 会将扩展名和Activate 注解缓存在cachedActivates; 另一部分是激活 names中满足条件的
  3. 对于第一部分, 如果names 中没有-default, 那么会去缓存 cachedActivates 中查找能适配group和URL的扩展名,如果匹配就激活.
  4. 对于第二部分, 遍历每个names, 如果names 中不包含-name, 那么激活
    [说明] default代表所有@Activate集合, -xxxx 则表示不激活xxxx
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<Class, T> activateExtensionsMap = new TreeMap<>(ActivateComparator.COMPARATOR);
        Set<String> loadedNames = new HashSet<>();
        Set<String> names = CollectionUtils.ofSet(values);
        if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
            getExtensionClasses();
            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;
                }
                if (isMatchGroup(group, activateGroup)
                        && !names.contains(name)
                        && !names.contains(REMOVE_VALUE_PREFIX + name)
                        && isActive(activateValue, url)
                        && !loadedNames.contains(name)) {
                    activateExtensionsMap.put(getExtensionClass(name), getExtension(name));
                    loadedNames.add(name);
                }
            }
            if (!activateExtensionsMap.isEmpty()) {
                activateExtensions.addAll(activateExtensionsMap.values());
            }
        }
        List<T> loadedExtensions = new ArrayList<>();
        // 遍历URL中指定key的值names, 对于每个值name, 如果name 不是以- 开头 而且 names中也不包含-name, 那么激活该name
        for (String name : names) {
            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;
    }
// 遍历@Activate 注解的values数组, 对于每个值用: 分隔,得到key, keyValue, 然后遍历URL中的参数(k,v形式), 如果参数k与key 相同或者后缀是.key 而且 keyValue 与v 相同或者 v 不为空, 那么返回true, 激活该扩展名
// 大概含义 就是 URL中包含@Activate 注解中values 声明的值,那么应该激活
private boolean isActive(String[] keys, URL url) {
        if (keys.length == 0) {
            return true;
        }
        for (String key : keys) {
            // @Active(value="key1:value1, key2:value2")
            String keyValue = null;
            if (key.contains(":")) {
                String[] arr = key.split(":");
                key = arr[0];
                keyValue = arr[1];
            }

            for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {
                String k = entry.getKey();
                String v = entry.getValue();
                if ((k.equals(key) || k.endsWith("." + key))
                        && ((keyValue != null && keyValue.equals(v)) || (keyValue == null && ConfigUtils.isNotEmpty(v)))) {
                    return true;
                }
            }
        }
        return false;
    }

ExtensionFactory的实现原理

在对扩展实例进行属性注入,使用到了objectFactory, 通过构造函数,我们知道它是ExtensionFactory的自适应实现

private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory =
                (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

ExtensionFactory 有三个实现类

  • AdaptiveExtensionFactory
  • SpiExtensionFactory
  • SpringExtensionFactory

其中, AdaptiveExtensionFactory 有@Adaptive注解, 通过上述 对getAdaptiveExtension 方法的流程分析, 如果有扩展类上有@Adaptive注解,则直接作为自适应实现, 所以 objectFactory 为 AdaptiveExtensionFactory

AdaptiveExtensionFactory

AdaptiveExtensionFactory 会在构造函数中初始化factories, 然后在执行 getExtension 方法时,调用每个factory的getExtension 方法. 那么factories是什么呢
通过查看ExtensionLoader 的 getSupportedExtensions 方法, factories 就是getExtensionClasses 方法的返回结果,而getExtensionClasses 是普通扩展类, 所以factories也就是 SpiExtensionFactory 和 SpringExtensionFactory
综上, AdaptiveExtensionFactory 是自适应实现,它是统管 SpiExtensionFactory 和 SpringExtensionFactory 的

@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {

    private final List<ExtensionFactory> factories;

    public AdaptiveExtensionFactory() {
        ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
        for (String name : loader.getSupportedExtensions()) {
            list.add(loader.getExtension(name));
        }
        factories = Collections.unmodifiableList(list);
    }

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        for (ExtensionFactory factory : factories) {
            T extension = factory.getExtension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

}


// ExtensionLoader 的 getSupportedExtensions 方法
public Set<String> getSupportedExtensions() {
        Map<String, Class<?>> clazzes = getExtensionClasses();
        return Collections.unmodifiableSet(new TreeSet<>(clazzes.keySet()));
    }

SpiExtensionFactory

getExtension 方法返回type类型 的自适应扩展实例

public class SpiExtensionFactory implements ExtensionFactory {

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
            if (!loader.getSupportedExtensions().isEmpty()) {
                return loader.getAdaptiveExtension();
            }
        }
        return null;
    }

}

SpringExtensionFactory

从 getExtension 方法可以看到 它是遍历SpringContext, 然后从Spring上下文中获取满足条件的实例的.那么Spring上下文是什么时候注入的, 是在 ReferenceBean, ServiceBean 和 ConfigCenterBean 初始化时注入的.
也就是dubbo 的属性注入不但可以注入其他的扩展点实例,还可以从spring容器中查找bean注入

public class SpringExtensionFactory implements ExtensionFactory {
    private static final Logger logger = LoggerFactory.getLogger(SpringExtensionFactory.class);

    private static final Set<ApplicationContext> CONTEXTS = new ConcurrentHashSet<ApplicationContext>();

    public static void addApplicationContext(ApplicationContext context) {
        CONTEXTS.add(context);
        if (context instanceof ConfigurableApplicationContext) {
            ((ConfigurableApplicationContext) context).registerShutdownHook();
            // see https://github.com/apache/dubbo/issues/7093
            DubboShutdownHook.getDubboShutdownHook().unregister();
        }
    }

    public static void removeApplicationContext(ApplicationContext context) {
        CONTEXTS.remove(context);
    }

    public static Set<ApplicationContext> getContexts() {
        return CONTEXTS;
    }

    // currently for test purpose
    public static void clearContexts() {
        CONTEXTS.clear();
    }

    @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)) {
            return null;
        }

        for (ApplicationContext context : CONTEXTS) {
            T bean = 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;
    }

}


public static <T> T getOptionalBean(ListableBeanFactory beanFactory, String beanName, Class<T> beanType) throws BeansException {
        if (beanName == null) {
            return getOptionalBeanByType(beanFactory, beanType);
        }

        T bean = null;
        try {
            bean = beanFactory.getBean(beanName, beanType);
        } catch (NoSuchBeanDefinitionException e) {
            // ignore NoSuchBeanDefinitionException
        } catch (BeanNotOfRequiredTypeException e) {
            // ignore BeanNotOfRequiredTypeException
            logger.warn(String.format("bean type not match, name: %s, expected type: %s, actual type: %s",
                    beanName, beanType.getName(), e.getActualType().getName()));
        }
        return bean;
    }

    private static <T> T getOptionalBeanByType(ListableBeanFactory beanFactory, Class<T> beanType) {
        // Issue : https://github.com/alibaba/spring-context-support/issues/20
        String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, beanType, true, false);
        if (beanNames == null || beanNames.length == 0) {
            return null;
        } else if (beanNames.length > 1) {
            throw new NoUniqueBeanDefinitionException(beanType, Arrays.asList(beanNames));
        }
        return (T) beanFactory.getBean(beanNames[0]);
    }

扩展点动态编译的实现

dubbo 动态生成的自适应扩展类只是字符串, 还需要编译才能得到真正的Class.

从如下代码可以看到,dubbo先是生成类的字符串code, 然后获取Compiler的自适应实现 来编译code 得到Class

private Class<?> createAdaptiveExtensionClass() {
        String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
        ClassLoader classLoader = findClassLoader();
        org.apache.dubbo.common.compiler.Compiler compiler =
                ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        return compiler.compile(code, classLoader);
    }

Compiler 有两个子类 AdaptiveCompiler, AbstractCompiler, AbstractCompiler 的也有两个子类 JavassistCompiler 和 JdkCompiler

和 ExtensionFactory 类似, AdaptiveCompiler 也是自适应实现,是管理 JavassistCompiler 和 JdkCompiler 的
AbstractCompiler 只是实现了一些通用逻辑,具体的编译工作由子类 JavassistCompiler 和 JdkCompiler 实现

AdaptiveCompiler

AdaptiveCompiler 的compile 方法, 先获取默认的编译器DEFAULT_COMPILER, 如果没有才调用ExtensionLoader的getDefaultExtension获得
getDefaultExtension 是加载cachedDefaultName 缓存的扩展名, 也就是扩展点@SPI注解中声明的值, Compiler声明的是javassist, 所以也就是如果没有设置 DEFAULT_COMPILER,默认使用的编译器是 JavassistCompiler
而通过代码搜索得知, DEFAULT_COMPILER 是在ApplicationConfig实例化的注入的,通过专栏的下一章得知 该属性是在解析配置文件中的<dubbo:application compiler=‘jdk’/> 赋值 的, 所以如果想使用 JdkCompiler 编译器,可以在配置文件配置

具体的编译工作由 JavassistCompiler 或者 JdkCompiler完成, 暂时就不再详细解析了,分别用到了 Javassist 和jdk原生的编译器.
后续再在Java字节码专栏进行详细讲解

@Adaptive
public class AdaptiveCompiler implements Compiler {

    private static volatile String DEFAULT_COMPILER;

    public static void setDefaultCompiler(String compiler) {
        DEFAULT_COMPILER = compiler;
    }

    @Override
    public Class<?> compile(String code, ClassLoader classLoader) {
        Compiler compiler;
        ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
        String name = DEFAULT_COMPILER; // copy reference
        if (name != null && name.length() > 0) {
            compiler = loader.getExtension(name);
        } else {
            compiler = loader.getDefaultExtension();
        }
        return compiler.compile(code, classLoader);
    }

}

//ExtensionLoader 的getDefaultExtension
public T getDefaultExtension() {
        getExtensionClasses();
        if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) {
            return null;
        }
        return getExtension(cachedDefaultName);
    }

最后

以上就是野性跳跳糖为你收集整理的dubbo 扩展点加载机制的全部内容,希望文章能够帮你解决dubbo 扩展点加载机制所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(95)

评论列表共有 0 条评论

立即
投稿
返回
顶部