1

dubbo spi机制

 2 years ago
source link: https://segmentfault.com/a/1190000041212860
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

dubbo spi机制

发布于 今天 10:52

框架设计的有弹性扩展性好会给使用者带来很大的方便,如果业务有新的变化只要使用新的实现去替换之前的实现,且框架如何去加载实现我们也不用太关注,显然这对我们开发者来说是非常友好的。java里的SPI是这个思想,dubbo里的SPI同样是这个思想,关于java里的spi因为会一次性将扩展点逻辑都执行而显得不够灵活,所以这里不再展开(可以参考java spi),这里重点说下dubbo里的SPI.

使用方式
1,在META-INF/services目录下根据接口的全路径名创建一个文件,文件内容为key=value的形式
2,ExtensionLoader.getExtensionLoader(xxx.class).getExtension("key")这样就可以获取到我们自定义的实现了。

实现分析
下面我们对ExtensionLoader.getExtensionLoader(xxx.class).getExtension(name)这行代码展开下。

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 interface!");
        }
        //判断给定接口上是否有SPI注解
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type(" + type +
                    ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }
        //先从map里获取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);
        }
        return loader;
    }

这全方法主要是获取ExtensionLoader:
1,对给定的type进行判断,一是判断是否是接口类型;二是判断接口上是否有SPI注解
2,根据给定的type先从Map获取对应的ExtensionLoader,要是没有进行创建。

接下来我们再看看getExtension(name)方法:

public T getExtension(String name) {

        if (name == null || name.length() == 0)
            throw new IllegalArgumentException("Extension name == null");
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        //先获取包装类Holder
        Holder<Object> holder = cachedInstances.get(name);
        if (holder == null) {
            cachedInstances.putIfAbsent(name, new Holder<Object>());
            holder = cachedInstances.get(name);
        }
        //再从包装类里获取实现类的实例,需要重点看下createExtension
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

getExtension(name)方法逻辑清晰明了:
1,先获取包装类Holder
2,从包装类Holder里获取具体实现类的实例。
实现类的实例是如何创建的呢?再看下createExtension(name)

private T createExtension(String name) {

        /**
         * getExtensionClasses会加载META-INF/services/,META-INF/dubbo/,META-INF/dubbo/internal/这三
         * 个目录下的所有类
         */
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
            //通过反射的方式创建name对应的实例,并放到map里,下次可以直接从map里取
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            /**
             * 如果实现类对象里有set方法,则框架还会根据依赖对象的实际情况进行自动注入的工作
             * 说明,只有满足下面两个条件才会进行注入:
             * 1,被依赖的对象也是在services,dubbo,internal这三个目录下
             * 2,set方法上没有使用了DisableInject注解
             */
            injectExtension(instance);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                    type + ")  could not be instantiated: " + t.getMessage(), t);
        }
    }

这方法的主要逻辑:
1,通过Class.forName加载META-INF/services/,META-INF/dubbo/,META-INF/dubbo/internal/这三个目录下的所有类
2,通过反射创建type对应的实例
3,如果符合注入条件,框架还会进行自动注入。

最后用一张图总结下整个过程:

创建完对象后都会将对象放入缓存方便下次直接获取。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK