我是靠谱客的博主 无心红酒,最近开发中收集的这篇文章主要介绍Dubbo-SPI扩展点加载机制SPIJava SPI,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

SPI

SPI 全称为 Service Provider Interface,是一种服务发现机制。当程序运行调用接口时,会根据配置文件或默认规则信息加载对应的实现类。所以在程序中并没有直接指定使用接口的哪个实现,而是在外部进行装配。
要想了解 Dubbo 的设计与实现,其中 Dubbo SPI 加载机制是必须了解的,在 Dubbo 中有大量功能的实现都是基于 Dubbo SPI 实现解耦,同时也使得 Dubbo 获得如此好的可扩展性。

Java SPI

通过完成一个 Java SPI 的操作来了解它的机制。

  • 创建一个 PersonService 接口及 hello 方法
  • 创建一个实现类 ChinesePerson
  • 创建 META-INF/services 目录,并在该目录下创建一个文件,文件名为 PersonService 的全限定名作为文件名
  • 在文件中添加实现类ChinesePerson的全限定名

   单元测试

   在pom文件中引入junit

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>

PersonService接口定义 

public interface PersonService {
    void hello();
}

 ChinesePerson实现类定义

public class ChinesePerson implements PersonService {
    @Override
    public void hello() {
        System.out.println("我是中国人");
    }
}

加载 SPI 的实现测试类

public class JavaSPITest {
    @Test
    public static void main(String[] args) {
        ServiceLoader<PersonService> serviceLoader = ServiceLoader.load(PersonService.class);

        // 遍历在配置文件中已配置的 AnimalService 的所有实现类
        for (PersonService person : serviceLoader) {
            person.hello();
        }
    }
}

 执行结果:

我是中国人

Process finished with exit code 0

Dubbo SPI

Dubbo SPI 相较于 Java SPI 更为强大,并且都是由自己实现的一套 SPI 机制。其中主要的改进和优化:

  • 相对于 Java SPI 一次性加载所有实现,Dubbo SPI 是按需加载,只加载需要使用的实现类。同时带有缓存支持。
  • 更为详细的扩展加载失败信息。
  • 增加了对扩展 IOC 和 AOP的支持。

Dubbo SPI 示例

Dubbo SPI 的配置文件放在 META-INF/dubbo 下面,并且实现类的配置方式采用 K-V 的方式,key 为实例化对象传入的参数,value 为扩展点实现类全限定名。例如 ChinesePerson 的配置文件内容:

 需要在接口上增加 @SPI 注解,@SPI 中可以指定 key 值

PersonService接口定义如下

@SPI("chinesePerson")
public interface PersonService {
    void hello();
}

实现类ChinesePerson定义 

public class ChinesePerson implements PersonService {
    @Override
    public void hello() {
        System.out.println("我是中国人");
    }
}

 DubboSPITest 单元测试类定义

public class DubboSPITest {
    @Test
    public void spi() {
        ExtensionLoader<PersonService> extensionLoader = ExtensionLoader.getExtensionLoader(PersonService.class);
        // 获取扩展类实现
        PersonService personService = extensionLoader.getExtension("chinesePerson");
        personService.hello();
    }
}

执行结果如下:

我是中国人

Process finished with exit code 0

 SPI 源码分析

获取 ExtensionLoader 实例

ExtensionLoader 类在dubbo的dubbo-common模块中定义,获取 ExtensionLoader 实例是通过上面 getExtensionLoader 方法,具体实现代码:

@SuppressWarnings("unchecked")
    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        if (type == null) {
            throw new IllegalArgumentException("Extension type == null");
        }

        // 检查type 必须为接口类型
        if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
        }
        // 检查type 类型上是否有注解 SPI
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type (" + type +
                    ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
        }

        // 从ConcurrentHashMap 中获取实例
        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);

        if (loader == null) {
            // 创建ExtensionLoader实例,并放入缓存
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        return loader;
    }

上面获取扩展类加载器过程主要是检查传入的 type 是否合法,以及从扩展类加载器缓存中是否存在当前类型的接口,如果不存在则添加当前接口至缓存中。
ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS 是扩展类加载器的缓存,它是以接口作为 key, 扩展类加载器作为 value 进行缓存。

获取扩展类对象

获取扩展类对象的方法ExtensionLoader#getExtension,在这里完成扩展对象的缓存及创建工作:

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

        //如果传入的参数为 true ,则获取默认扩展类对象操作
        if ("true".equals(name)) {
            return getDefaultExtension();
        }

        //获取扩展对象,Holder 里的 value 属性保存着扩展对象实例
        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;
    }

获取 holder 对象是从缓存ConcurrentMap<String, Holder<Object>> cachedInstances中获取,如果不存在,则以扩展名 key,创建一个 Holder 对象作为 value,设置到扩展对象缓存。
如果是新创建的扩展对象实例,那么 holder.get() 一定是 null ,扩展对象为空时,经过双重检查锁,创建扩展对象。

创建扩展对象

创建扩展对象过程

 @SuppressWarnings("unchecked")
    private T createExtension(String name, boolean wrap) {
        // 从全部扩展类中,获取当前扩展名对应的扩展类
        // 读取 META-INF/dubbo/external/
        // META-INF/dubbo/internal/
        // META-INF/services/
        Class<?> clazz = getExtensionClasses().get(name);

        if (clazz == null) {
            throw findException(name);
        }
        try {

            // 从缓存ConcurrentMap<Class<?>, Object>中获取扩展实例,及设置扩展实例缓存
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.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 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 加载全部扩展类

   /**
     * synchronized in getExtensionClasses
     * 读取 META-INF/dubbo/external/
     * META-INF/dubbo/internal/
     * 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;
    }

最后

以上就是无心红酒为你收集整理的Dubbo-SPI扩展点加载机制SPIJava SPI的全部内容,希望文章能够帮你解决Dubbo-SPI扩展点加载机制SPIJava SPI所遇到的程序开发问题。

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

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

评论列表共有 0 条评论

立即
投稿
返回
顶部