Dubbo的SPI实现原理源码分析

SPI简介

  • SPI全称Service Provider Interface(服务提供者接口)是一种服务发现机制,本质上,是将服务的实现类的全限定名配置在文件中,通过服务器加载类读取配置文件,加载实现类,这样在运行时,动态的为接口替换实现类。Dubbo并没有采用Java原生的SPI的实现方式,而是对其进行了增强,在Dubbo框架的实现中,SPI是一个非常重要的模块。接下来,我们一起分析Dubbo的SPI机制和实现原理,

源码分析(apache dubbo 2.7.8)

  1. Dubbo的SPI主要实现类ExtensionLoader,一般情况,我们通过ExtensionLoader的getExtensionLoader方法获取到一个实例,然后在通过getExtension方法获取到对应的拓展类对象实例。那我们就从这两个方法开始入手分析。
    @SuppressWarnings("unchecked")
    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!");
        }
        // 接口上需要标识@SPI注解
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type (" + type +
                    ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
        }

        // 从缓存中查找,没有就新建一个,放入到缓存map中
        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        if (loader == null) {
            // putIfAbsent,保证了线程安全性
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        return loader;
    }
  1. 这里直接new了一个ExtensionLoader实例,我们再看一下new的过程
    private ExtensionLoader(Class<?> type) {
        // type就是我们传入的Class类型
        this.type = type;
        // 这里获取了一个objectFactory,对应的是ExtensionFactory的自适应的扩展点实现,对于获取自适应扩展的实现,后续会单独讲解,这里先跳过
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }
  1. 然后调用getExtension方法
    /**
     * 通过给定的名称找到接口的实现类,找不到会抛出异常
     */
    @SuppressWarnings("unchecked")
    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");
        }
        // 查找默认的实现,这里可以看到,如果传入的name为true,就会直接找默认的实现类,也就是SPI注解中的value值对应的name
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        // holder,用于持有目标对象
        final Holder<Object> holder = getOrCreateHolder(name);
        Object instance = holder.get();
        // 双重检查,创建出扩展实例,设置到holder中
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    // 创建拓展实例
                    instance = createExtension(name, wrap);
                    // 设置实例到holder中
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }
  1. 继续跟踪createExtension方法
    /**
     * 1. 通过 getExtensionClasses 获取所有的拓展类
     * 2. 通过反射创建拓展对象
     * 3. 向拓展对象中注入依赖
     * 4. 将拓展对象包裹在相应的 Wrapper 对象中
     * @param name
     * @param wrap
     * @return
     */
    @SuppressWarnings("unchecked")
    private T createExtension(String name, boolean wrap) {
        // 从配置文件中加载所有的拓展类,可得到“配置项名称”到“配置类”的映射关系表
        Class<?> clazz = getExtensionClasses().get(name);
        // 找不到该name对应的扩展类Class对象,直接抛出异常
        if (clazz == null) {
            throw findException(name);
        }
        try {
            // 先从map中根据类型查找对应实例
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                // 通过反射创建出实例,保存到map中
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            // 向实例中注入依赖, 通过setter方式注入,目前只支持setter方式注入
            injectExtension(instance);

            // 如果有包装,默认传过来的是true
            if (wrap) {

                List<Class<?>> wrapperClassesList = new ArrayList<>();
                // 扩展实例中包含该类型的单个参数的构造函数,会认为是包装类,将会添加到cacheWrapperClasses集合中
                if (cachedWrapperClasses != null) {
                    wrapperClassesList.addAll(cachedWrapperClasses);
                    wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                    // 倒序
                    Collections.reverse(wrapperClassesList);
                }

                if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                    // 循环创建Wrapper实例
                    for (Class<?> wrapperClass : wrapperClassesList) {
                        Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                        if (wrapper == null
                                || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
                            // 将当前 instance 作为参数传给 Wrapper 的构造方法,并通过反射创建 Wrapper 实例。
                            // 然后向 Wrapper 实例中注入依赖,最后将 Wrapper 实例再次赋值给 instance 变量
                            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                        }
                    }
                }
            }
            // 如果当前实例是Lifecycle的子类,则调用初始化方法
            initExtension(instance);
            // 返回经过层层包装以后的实例对象
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                    type + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }
  1. 这一段方法比较长, 我们先分析getExtensionClasses方法
    private Map<String, Class<?>> getExtensionClasses() {
        // 从缓存中获取已加载的拓展类,cachedClasses缓存了name到Class的映射
        Map<String, Class<?>> classes = cachedClasses.get();
        // 双重检查
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    // 加载拓展类并设置到缓存中
                    classes = loadExtensionClasses();
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }
    
    /**
     * synchronized in getExtensionClasses
     * 同步获取扩展类
     */
    private Map<String, Class<?>> loadExtensionClasses() {
        // 缓存默认实现类的名称,就是指SPI注解上如果存在value,并且只有一个,就是设置到cacheDefaultName,默认的缓存实现名称
        cacheDefaultExtensionName();

        Map<String, Class<?>> extensionClasses = new HashMap<>();
        // stategies,加载策略, 这里是通过Java的SPI方式,获取到LoadingStrategy的实现,按照一定的顺序从类路径下的固定位置去加载扩展实现类
        // 优先级顺序:ServicesLoadingStrategy < DubboLoadingStrategy < DubboInternalLoadingStrategy
        // 上面对应的类路径分别是:META-INF/services/ , META-INF/dubbo/ , META-INF/dubbo/internal/
        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;
    }
  1. 这里根据class类型开始遍历加载策略,从不同的路径下分别去加载给定的class的实现类,我们继续来分析loadDirectory的逻辑
    /**
     * @param extensionClasses 用来保存扩展类的name到Class的映射
     * @param dir 文件目录
     * @param type type的全限定名
     * @param extensionLoaderClassLoaderFirst 是否优先使用加载ExtensionLoader的类加载器加载, 默认是false
     * @param overridden 指示当前加载策略是否支持覆盖其他较低优先级的实例
     * @param excludedPackages 需要排除的包,这里为空
     */
    private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type,
                               boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) {
        // fileName = 文件夹路径 + type的全限定名
        String fileName = dir + type;
        try {
            Enumeration<java.net.URL> urls = null;
            // 获取类加载器,先获取当前线程持有的类加载器,没有再获取加载ExtensionLoader的类加载器,没有再使用系统类加载器
            ClassLoader classLoader = findClassLoader();

            // 如果为true,先使用加载ExtensionLoader的类加载器加载,这里为false,直接跳过
            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);
        }
    }
    
    
    // 加载资源    
    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;
                // 按行读取配置内容
                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
                                name = line.substring(0, i).trim();
                                // 获取到全限定名
                                line = line.substring(i + 1).trim();
                            }
                            // 排除指定的包
                            if (line.length() > 0 && !isExcluded(line, excludedPackages)) {
                                // 加载Class
                                loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name, overridden);
                            }
                        } catch (Throwable t) {
                            // 有异常都先保存到exceptions集合中, 待后续使用
                            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. 这里分析了从不同路径下去加载资源,获取文件中没有每一行数据,分割后,开始准备加载Class,既然文件中配置的是类的全限定名,那我们一定想到就是利用反射方式去加载得到Class对象,我们这里继续分析
    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.");
        }
        // 检测目标类上是否有Adaptive注解
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            // 设置 cachedAdaptiveClass缓存,注意这里只允许实现类只有一个类上有Adaptive注解,否则会抛出异常
            cacheAdaptiveClass(clazz, overridden);
        // 检测clazz是否是Wrapper类型(含有单个该类型参数的构造方法,即为包装类型),如果是,就添加到cachedWrapperClasses集合中
        } else if (isWrapperClass(clazz)) {
            cacheWrapperClass(clazz);
        } else {
            // 检测 clazz 是否有默认的构造方法,如果没有,则抛出异常
            clazz.getConstructor();
            if (StringUtils.isEmpty(name)) {
                // 没有配置name,则按照注解Extension(已废弃),类名等方式重新获取到name
                name = findAnnotationName(clazz);
                if (name.length() == 0) {
                    throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
                }
            }

            // 切分name
            String[] names = NAME_SEPARATOR.split(name);
            if (ArrayUtils.isNotEmpty(names)) {
                // 取第一个名称,缓存到cachedActivates中(name到Activate的映射)
                cacheActivateClass(clazz, names[0]);
                for (String n : names) {
                    // 缓存Class到name的映射
                    cacheName(clazz, n);
                    // 保存name到Class的映射
                    saveInExtensionClass(extensionClasses, clazz, n, overridden);
                }
            }
        }
    }
    
    /**
     * put clazz in extensionClasses
     */
    private void saveInExtensionClass(Map<String, Class<?>> extensionClasses, Class<?> clazz, String name, boolean overridden) {
        Class<?> c = extensionClasses.get(name);
        if (c == null || overridden) {
            // 保存name到Clazz的映射关系
            extensionClasses.put(name, clazz);
        } else if (c != clazz) {
            // 如果同一个name对应不同的Class,则抛出异常
            String duplicateMsg = "Duplicate extension " + type.getName() + " name " + name + " on " + c.getName() + " and " + clazz.getName();
            logger.error(duplicateMsg);
            throw new IllegalStateException(duplicateMsg);
        }
    }
  1. 以上分析了getExtensionClasses方法,是如何将type的扩展类加载出来,保存到extensionClasses中,我们回到createExtension方法中,继续分析injectExtension过程
    /**
     * 注入实例,类似于Spring的IOC,但是只支持setter方式注入
     */
    private T injectExtension(T instance) {
        // 获取对象工厂,如果为null,直接返回该实例
        if (objectFactory == null) {
            return instance;
        }

        try {
            // 遍历目标类的所有方法
            for (Method method : instance.getClass().getMethods()) {
                // 检测方法是否以 set 开头,且方法仅有一个参数,且方法访问级别为 public
                if (!isSetter(method)) {
                    continue;
                }
                /**
                 * 检查DisableInject,看看是否需要对该属性进行自动注入
                 */
                if (method.getAnnotation(DisableInject.class) != null) {
                    continue;
                }
                Class<?> pt = method.getParameterTypes()[0];
                // 参数是否是原生类型
                if (ReflectUtils.isPrimitives(pt)) {
                    continue;
                }

                try {
                    // 通过方法,获取到属性名,如果方法名是setProtocol,那么property就是protocol
                    String property = getSetterProperty(method);
                    // 从 ObjectFactory 中获取依赖对象(自适应的扩展点实例)
                    Object object = objectFactory.getExtension(pt, property);
                    if (object != null) {
                        // 反射调用setter方法,为属性赋值
                        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;
    }
  1. 上面分析了Dubbo的扩展实例如何完成依赖注入的功能,下面继续来分析dubbo是如何对扩展类进行包装的,可以看作是dubbo的aop实现方式,我们截取了createExtension方法这一块内容相关的片段来分析
    // 如果有包装, 默认传的是true
    if (wrap) {
        List<Class<?>> wrapperClassesList = new ArrayList<>();
        if (cachedWrapperClasses != null) {
            wrapperClassesList.addAll(cachedWrapperClasses);
            // 先排序, order大的在后面,例如QosProtocolWrapper,ProtocolFilterWrapper,ProtocolListenerWrapper
            wrapperClassesList.sort(WrapperComparator.COMPARATOR);
            // 倒序,ProtocolListenerWrapper,ProtocolFilterWrapper,QosProtocolWrapper
            Collections.reverse(wrapperClassesList);
        }
        if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
            // 循环创建Wrapper实例,把一个类的实例当作后面类的构造函数参数,实例化以后再注入
            // 这一段代码以Protocol举例:
            // Protocol dubboProtocol           = new DubboProtocol();
            // Protocol protocolListenerWrapper = new ProtocolListenerWrapper(dubboProtocol);
            // Protocol protocolFilterWrapper   = new ProtocolFilterWrapper(protocolListenerWrapper);
            // Protocol qosProtocolWrapper      = new QosProtocolWrapper(protocolFilterWrapper);
            for (Class<?> wrapperClass : wrapperClassesList) {
                Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                if (wrapper == null
                    || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
                    // 将当前 instance 作为参数传给 Wrapper 的构造方法,并通过反射创建 Wrapper 实例
                    // 然后向 Wrapper 实例中注入依赖,最后将 Wrapper 实例再次赋值给 instance 变量
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
        }
    }
    // 如果当前实例是Lifecycle的子类,则调用初始化方法
    initExtension(instance);
    // 返回经过层层包装以后的实例对象
    return instance;
  1. 经过一层一层包装后,得到的实例对象,就是包装后的对象。

总结

  • 以上内容就是对Dubbo的SPI的过程的具体分析,读者可以跟着源码一步步去分析,如果有错误的地方,烦请指出来,望读者不吝赐教。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值