我是靠谱客的博主 火星上月亮,最近开发中收集的这篇文章主要介绍【Dubbo】Dubbo SPI自定义配置(源码)目录一、基本介绍二、自定义一波三、看看源码 ,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

目录

一、基本介绍

二、自定义一波

三、看看源码 

1. ExtensionLoader#getExtensionLoader(Fly.class)

2. 再到ExtensionLoader#getExtension

3.  ExtensionLoader#getExtensionClasses

4. ExtensionLoader#loadExtensionClasses 

5. ExtensionLoader#loadDirectory 

6. loadResource->loadClass 


首先看下官方怎么玩的

一、基本介绍

不同于Java SPI,Dubbo SPI是按需加载。通过名字去文件里面找到对应的实现类全限定名然后加载实例化。配置文件里面存放的是键值对,例如:

对于配置目录,Dubbo 对配置文件目录的约定(不同于 Java SPI ,Dubbo 分为了三类目录)

1. META-INF/services/ 目录:该目录下的 SPI 配置文件是为了用来兼容 Java SPI 

2. META-INF/dubbo/ 目录:该目录存放用户自定义的 SPI 配置文件

3. META-INF/dubbo/internal/ 目录:该目录存放 Dubbo 内部使用的 SPI 配置文件


二、自定义一波

那么自定义一波,首先写好接口和实现类:

注意接口写个@SPI注解表明要用SPI机制,值可以加,表示默认值(像用默认netty server那样)

/**
 * 测试Dubbo SPI
 * @author ZRH
 * @version 1.0.0
 * @date 2020/10/16
 */
@SPI
public interface Fly {
    void fly();
}
public class Bird implements Fly{
    @Override
    public void fly() {
        System.out.println("Bird can fly");
    }
}
public class Plane implements Fly{
    @Override
    public void fly() {
        System.out.println("Plane can fly");
    }
}

然后写好测试类

import com.example.dubbo.service.Fly;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.junit.jupiter.api.Test;
public class DubboSPITests {

    @Test
    public void test() {
        ExtensionLoader<Fly> extensionLoader = ExtensionLoader.getExtensionLoader(Fly.class);
        Fly bird = extensionLoader.getExtension("bird");
        bird.fly();

        Fly plane = extensionLoader.getExtension("plane");
        plane.fly();
    }
}

写好配置(特别注意配置文件名为接口全类名):

跑起来:


三、看看源码 

1. ExtensionLoader#getExtensionLoader(Fly.class)

    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {

        // 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() + "!");
        }

        // 从缓存里面找是否已经存在这个类型的 ExtensionLoader
        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);

        //  如果没有就新建一个塞入缓存。最后返回接口类对应的 ExtensionLoader
        if (loader == null) {
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        return loader;
    }

 三个判断,可以看到,得有@SPI注解才能用

    private static <T> boolean withExtensionAnnotation(Class<T> type) {
        return type.isAnnotationPresent(SPI.class);
    }

2. 再到ExtensionLoader#getExtension

    public T getExtension(String name) {
        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);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }
    private T createExtension(String name) {

        // 获取实现类,com.example.dubbo.service.Bird
        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);
            }

            // setter依赖注入
            injectExtension(instance);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;

            // 如果有包装类则包装
            if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                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 + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }

3.  ExtensionLoader#getExtensionClasses

怎么获取到它的实现类的呢?看ExtensionLoader#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;
    }

4. ExtensionLoader#loadExtensionClasses 

获取缓存类,有则直接返回缓存类,去那里找,没有则跳到ExtensionLoader#loadExtensionClasses

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

        // 如果SPI注释有默认值,则缓存起来
        cacheDefaultExtensionName();

        Map<String, Class<?>> extensionClasses = new HashMap<>();
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache"
            , "com.alibaba"));
        loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache"
            , "com.alibaba"));
        loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache"
            , "com.alibaba"));
        return extensionClasses;
    }

然后加载文件,路径就是

5. ExtensionLoader#loadDirectory 

 记住这里type.getName是Class获取全类名,然后看ExtensionLoader#loadDirectory

    private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
        // 文件名出来了,META-INF/dubbo/com.example.dubbo.service.Fly
        String fileName = dir + type;
        try {
            Enumeration<java.net.URL> urls;
            ClassLoader classLoader = findClassLoader();
            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);
                }
            }
        } catch (Throwable t) {
            logger.error("Exception occurred when loading extension class (interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }

6. loadResource->loadClass 

文件名出来了,这也就是为什么,我们自定义扩展,文件名得是接口的全类名。接着跳到loadResource->loadClass

    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.");
        }

        // 如果有@Adaptive注解,则缓存下类
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            cacheAdaptiveClass(clazz);
        } 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)) {

                // 有@Activate注解也缓存下
                cacheActivateClass(clazz, names[0]);

                for (String n : names) {
                    // 记录全类名到类名的映射
                    // 比如class com.example.dubbo.service.Bird -> Bird
                    cacheName(clazz, n);
                    
                    // put clazz in extensionClasses
                    saveInExtensionClass(extensionClasses, clazz, n);
                }
            }
        }
    }

至于@Adaptive、包装类、@Activate就留到下一章节

最后

以上就是火星上月亮为你收集整理的【Dubbo】Dubbo SPI自定义配置(源码)目录一、基本介绍二、自定义一波三、看看源码 的全部内容,希望文章能够帮你解决【Dubbo】Dubbo SPI自定义配置(源码)目录一、基本介绍二、自定义一波三、看看源码 所遇到的程序开发问题。

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

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

评论列表共有 0 条评论

立即
投稿
返回
顶部