Dubbo SPI 源码解析

2021/4/25 20:28:41

本文主要是介绍Dubbo SPI 源码解析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Dubbo SPI

基于Java的SPI机制,Dubbo在其思想的基础上自己实现了一套SPI机制,弥补了Java SPI的缺点,并进行了相应的扩展。

1. Dubbo SPI 改进了 Java SPI 的以下问题:

  • Java SPI是一次性加载、实例化所有的扩展点实现,不支持根据key值去加载、实例化指定的扩展点实现。
  • Java SPI不支持IOC、AOPDubbo SPI则通过自动包装自动装配等特性实现了IOC、AOP
  • ServerLoader在加载类的过程中如果出现异常无法加载没有相关的异常抛出,导致一旦出现问题需要花时间进行定位。

2. Dubbo SPI 的三个特性

2.1. 自动包装

基于装饰器设计模式,通过创建、配置Wrapper类,从而实现对实现类的增强。我们可以将一些公共的逻辑放置在Wrapper类中,从而实现了公共逻辑与业务逻辑的解耦,同时也更易于扩展和维护。

2.2. 自动装配

类似于Spring的IOC依赖注入的功能,在加载扩展点的实现类时,会遍历所有setter方法,尝试着为其注入其他扩展点依赖。

2.3. 自适应

Dubbo使用URL对象传递配置信息,通过在URL对象中配置对应的key-value键值对,从而实现在调用相应方法时能够通过URL中的配置匹配对应的扩展点实现。

2.4. 自动激活

当我们想通过一个配置激活多个扩展点实现时,可以通过@Activate注解来实现,从而简化了配置。

3. 代码示例:

3.1. 车辆接口
package com.dubbo.service;

import org.apache.dubbo.common.extension.SPI;

@SPI
public interface ICarService {
    void speed();
}
3.2. 车辆接口的两个实现类AudiServiceBmwService
package com.dubbo.service.impl;

import com.dubbo.service.ICarService;

public class AudiService implements ICarService {
    @Override
    public void speed() {
        System.out.println("audi drive speed 100KM/H");
    }
}


public class BMWService implements ICarService {
    @Override
    public void speed() {
        System.out.println("bmw drive speed 200KM/H");
    }
}
3.3. 配置文件

resources\META-INF\services\com.dubbo.service.ICarService

audi=com.dubbo.service.impl.AudiService
bmw=com.dubbo.service.impl.BMWService
3.4. 测试方法
private static void dubboSpi() {
    ExtensionLoader<ICarService> extensionLoader = ExtensionLoader.getExtensionLoader(ICarService.class);
    ICarService carService = extensionLoader.getExtension("audi");
    carService.speed();
}

// 输出
audi drive speed 100KM/H

4. 源码解析

4.1 获取扩展点加载器

Main方法

ExtensionLoader.getExtensionLoader(ICarService.class);
ICarService carService = extensionLoader.getExtension("audi");

org.apache.dubbo.common.extension.ExtensionLoader

// key:扩展点接口-扩展点加载器
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>();

// 获取扩展点加载器
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    ...
    // 尝试从缓存中获取扩展点加载器
    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;
}

获取到扩展点加载器后,调用扩展点加载器的getExtension方法获取扩展点实现对象

public T getExtension(String name) {
    if (StringUtils.isEmpty(name)) {
        throw new IllegalArgumentException("Extension name == null");
    }

    // 如果传入name为true,则获取默认扩展实现
    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);
                // 放入到持有器中
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}

先从缓存中获取扩展点实现实例,如果没有获取到则调用createExtension()进行创建

private T createExtension(String name) {
    // 获取扩展点实现类(这里下面会详细说下)
    Class<?> clazz = getExtensionClasses().get(name);
    if (clazz == null) {
        throw findException(name);
    }
    try {
        // 从缓存中尝试获取该类对应的实例
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
            // 没有获取到,则创建一个实例放入缓存中
            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        // 依赖注入(这里下面会详细说)
        injectExtension(instance);

        // 如果有包装类,则递归创建包装类实例,并将当前实例注入包装类实例中
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (CollectionUtils.isNotEmpty(wrapperClasses)) {
            for (Class<?> wrapperClass : wrapperClasses) {
                instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
            }
        }

        // 如果是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);
    }
}

下面是getExtensionClasses()源码解析,加载指定路径下实现点文件中的别名与实现类

Java SPI会扫描META-INF/services/目录下的文件从而检测扩展点实现类,Dubbo SPI该步骤是在getExtensionClasses()方法中加载扩展点实现类

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

加载META-INF/dubbo/internal/META-INF/dubbo/META-INF/services/目录下该扩展点文件中的别名与对应的实现类,放入到缓存Map中,并返回

private Map<String, Class<?>> loadExtensionClasses() {
    // 缓存当前扩展点的默认实现
    cacheDefaultExtensionName();

    Map<String, Class<?>> extensionClasses = new HashMap<>();
    // 加载 META-INF/dubbo/internal/ 目录下当前扩展点对应的具体实现类列表 (下面会详说)
    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName(), true);
    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"), true);

    // 加载 META-INF/dubbo/ 目录下当前扩展点对应的具体实现类列表
    loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
    loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));

    // 加载 META-INF/services/ 目录下当前扩展点对应的具体实现类列表
    loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
    loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));

    // 返回别名-扩展点实现类Map,这里如果别名一致会覆盖,后来的会覆盖前面的
    return extensionClasses;
}

loadDirectory()方法中,会通过类加载器加载扩展点文件,并解析其中的内容,存入extensionClasses Map缓存中

private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type, boolean extensionLoaderClassLoaderFirst) {
        // 获取文件全路径名
        String fileName = dir + type;
        try {
            Enumeration<java.net.URL> urls = null;
            // 获取加载ExtensionLoader类的类加载器
            ClassLoader classLoader = findClassLoader();
            
            // 如果是第一次加载,则获取类加载器
            if (extensionLoaderClassLoaderFirst) {
                // 获取加载ExtensionLoader类的类加载器
                ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
                // 如果当前系统的类加载器不是加载ExtensionLoader类的类加载器时,则通过加载ExtensionLoader类的类加载器获取文件对应的url访问路径列表
                if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
                    urls = extensionLoaderClassLoader.getResources(fileName);
                }
            }

            // 如果urls为空
            if(urls == null || !urls.hasMoreElements()) {
                if (classLoader != null) {
                    // 加载文件对应的url访问路径列表
                    urls = classLoader.getResources(fileName);
                } else {
                    // 使用系统类加载器、根类加载器加载文件对应的url访问路径列表
                    urls = ClassLoader.getSystemResources(fileName);
                }
            }

            if (urls != null) {
                // 遍历url访问路径列表
                while (urls.hasMoreElements()) {
                    java.net.URL resourceURL = urls.nextElement();
                    // 加载文件内容(下面会详说)
                    loadResource(extensionClasses, classLoader, resourceURL);
                }
            }
        } catch (Throwable t) {
            logger.error("Exception occurred when loading extension class (interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }

解析实现点文件,获取别名与对应的实现类全名,加载实现类,放入到extensionClasses缓存Map中

private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
    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) {
                    try {
                        // 假设 audi=com.lbb.service.impl.AudiService =》则name=audi, line=com.lbb.service.impl.AudiService
                        String name = null;
                        int i = line.indexOf('=');
                        if (i > 0) {
                            name = line.substring(0, i).trim();
                            line = line.substring(i + 1).trim();
                        }
                        if (line.length() > 0) {
                            // 解析别名、加载实现类,并放入到extensionClasses缓存Map中(下面详说)
                            loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
                        }
                    } 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);
    }
}

查看是否存在@Adaptive注解、是否是包装类,如果都不是则解析别名与实现类,放入缓存Map中

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) 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)) {
        // 如果存在@Adaptive注解,则缓存到cachedAdaptiveClass中
        cacheAdaptiveClass(clazz);
    } else if (isWrapperClass(clazz)) {
        // 如果这个类的构造方法的参数类型是实现点接口,则说明是包装类,缓存到cachedWrapperClasses List中
        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);
                // 缓存别名与实现类到extensionClasses 缓存Map中
                saveInExtensionClass(extensionClasses, clazz, n);
            }
        }
    }
}

下面是injectExtension()的源码解析,通过获取set方法参数与名称,获取对应注入类实例,反射注入依赖对象

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

    try {
        // 遍历实现类中的所有方法
        for (Method method : instance.getClass().getMethods()) {
            // 如果不是set方法,则继续遍历
            if (!isSetter(method)) {
                continue;
            }

            // 如果方法上存在@DisableInject注解,则继续遍历
            if (method.getAnnotation(DisableInject.class) != null) {
                continue;
            }

            // 获取set方法的第一个参数的类型
            Class<?> pt = method.getParameterTypes()[0];
            // 如果该参数类型是基础数据类型,则继续遍历
            if (ReflectUtils.isPrimitives(pt)) {
                continue;
            }

            try {
                // 解析set后的名称,setCarService =》 carService
                String property = getSetterProperty(method);
                // 根据解析后的名称去工厂中获取对应的扩展类对象
                Object object = objectFactory.getExtension(pt, property);
                if (object != null) {
                    // 通过反射调用set方法进行注入
                    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;
}

下面是initExtension()方法的源码,调用实现类对象的初始化方法进行初始化

private void initExtension(T instance) {
    // 检测是否实现了Lifecycle接口
    if (instance instanceof Lifecycle) {
        Lifecycle lifecycle = (Lifecycle) instance;
        // 如果实现了,则调用其初始化方法
        lifecycle.initialize();
    }
}


这篇关于Dubbo SPI 源码解析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程