千家信息网

Dubbo的SPI机制介绍以及SPI加载class的方法

发表于:2025-02-03 作者:千家信息网编辑
千家信息网最后更新 2025年02月03日,这篇文章主要讲解了"Dubbo的SPI机制介绍以及SPI加载class的方法",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"Dubbo的SPI机制介绍以
千家信息网最后更新 2025年02月03日Dubbo的SPI机制介绍以及SPI加载class的方法

这篇文章主要讲解了"Dubbo的SPI机制介绍以及SPI加载class的方法",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"Dubbo的SPI机制介绍以及SPI加载class的方法"吧!

1、Dubbo的SPI例子

@SPIpublic interface Robot {    void sayHello();}public class OptimusPrime implements Robot{    @Override    public void sayHello() {        System.out.println("Hello, I am Optimus Prime.");    }}public class Bumblebee implements Robot{    @Override    public void sayHello() {        System.out.println("Hello, I am Bumblebee.");    }}
public class DubboSPITest {    @Test    public void sayHelloDubbo() throws Exception {        ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class);        Robot optimusPrime = extensionLoader.getExtension("optimusPrime");        optimusPrime.sayHello();        Robot bumblebee = extensionLoader.getExtension("bumblebee");        bumblebee.sayHello();    }}
输出:Hello, I am Optimus Prime.Hello, I am Bumblebee.

2、Dubbo的SPI源码分析

ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class);public static  ExtensionLoader getExtensionLoader(Class type) {    if (type == null)        throw new IllegalArgumentException("Extension type == null");    if (!type.isInterface()) {        throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");    }    if (!withExtensionAnnotation(type)) {        throw new IllegalArgumentException("Extension type(" + type +                ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");    }    // 从缓存中获取与拓展类对应的ExtensionLoader    ExtensionLoader loader = (ExtensionLoader) EXTENSION_LOADERS.get(type);    if (loader == null) {        // 若缓存未命中,则创建一个新的实例,先简单看下new ExtensionLoader(type)        EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));        loader = (ExtensionLoader) EXTENSION_LOADERS.get(type);    }    return loader;}

这里的objectFactory创建可以参考: Dubbo的SPI机制分析3-Dubbo的IOC依赖注入

 private ExtensionLoader(Class type) {    this.type = type;    // 这里的type是Robot.class,所以会执行后面的代码,加载并创建SpiExtensionFactory和SpringExtensionFactory,    objectFactory = (type == ExtensionFactory.class ? null :                              ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());}
Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
public T getExtension(String name) {    if ("true".equals(name)) {        return getDefaultExtension();    }    // Holder也是用于持有对象的,用作缓存    Holder holder = cachedInstances.get(name);    if (holder == null) {        cachedInstances.putIfAbsent(name, new Holder());        holder = cachedInstances.get(name);    }    Object instance = holder.get();    if (instance == null) {        synchronized (holder) {            instance = holder.get();            if (instance == null) {                // 创建拓展实例,下面分析createExtension(name)                instance = createExtension(name);                holder.set(instance);            }        }    }    return (T) instance;}

这里依赖注入可以参考: Dubbo的SPI机制分析3-Dubbo的IOC依赖注入

private T createExtension(String name) {    // 从配置文件中加载所有的拓展类,可得到"配置项名称"到"配置类"的映射关系表    // 加载完后根据name获取,得到形如com.alibaba.dubbo.demo.provider.spi.OptimusPrime    // 待会重点分析getExtensionClasses(),删去了一些非关键代码    Class clazz = getExtensionClasses().get(name);    try {        // 也是尝试先从缓存获取,获取不到通过反射创建一个并放到缓存中        T instance = (T) EXTENSION_INSTANCES.get(clazz);        if (instance == null) {            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());            instance = (T) EXTENSION_INSTANCES.get(clazz);        }        // 依赖注入和cachedWrapperClasses,后面分析        injectExtension(instance);        Set> wrapperClasses = cachedWrapperClasses;        if (wrapperClasses != null && !wrapperClasses.isEmpty()) {           for (Class wrapperClass : wrapperClasses) {              instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));           }        }        return instance;    } }
 private Map> getExtensionClasses() {    // 从缓存中获取映射关系表    Map> classes = cachedClasses.get();    // 双重检查    if (classes == null) {        synchronized (cachedClasses) {            classes = cachedClasses.get();            if (classes == null) {                // 若缓存中没有,去加载映射关系                classes = loadExtensionClasses();                cachedClasses.set(classes);            }        }    }    return classes;}
private Map> loadExtensionClasses() {    // 获取SPI注解,这里的type是在调用getExtensionLoader方法时传入的    final SPI defaultAnnotation = type.getAnnotation(SPI.class);    if (defaultAnnotation != null) {        String value = defaultAnnotation.value();        if ((value = value.trim()).length() > 0) {            String[] names = NAME_SEPARATOR.split(value);            if (names.length > 1) {                // 抛异常            }            // 获取@SPI注解中的值,并缓存起来,可以关注下,后面会用到            if (names.length == 1) cachedDefaultName = names[0];        }    }    Map> extensionClasses = new HashMap>();    // 加载指定文件夹下的配置文件,META-INF/services/、META-INF/dubbo/、META-INF/dubbo/internal/    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);    // 我是放在这个目录下的    loadDirectory(extensionClasses, DUBBO_DIRECTORY);    loadDirectory(extensionClasses, SERVICES_DIRECTORY);    return extensionClasses;}
private void loadDirectory(Map> extensionClasses, String dir) {    // fileName是META-INF/dubbo/com.alibaba.dubbo.demo.provider.spi.Robot    String fileName = dir + type.getName();    try {        Enumeration urls;        ClassLoader classLoader = findClassLoader();        // 根据文件名加载所有同名文件        if (classLoader != null) {            urls = classLoader.getResources(fileName);        } else {            urls = ClassLoader.getSystemResources(fileName);        }        if (urls != null) {            while (urls.hasMoreElements()) {                // 一个resourceURL就是一个文件                java.net.URL resourceURL = urls.nextElement();                loadResource(extensionClasses, classLoader, resourceURL);            }        }    } }
private void loadResource(Map> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {    try {        BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));        try {            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 = line.substring(0, i).trim();                            line = line.substring(i + 1).trim();                        }                        if (line.length() > 0) {                            // 真正的去加载类                            loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);                        }                    }                }            }        }    } }

标注1,这里的可以参考: Dubbo的SPI机制分析2-Adaptive详解

标注2,这里的可以参考: Dubbo的SPI机制分析4-Dubbo通过Wrapper实现AOP

标注3,这里的cachedActivates可以参考: Dubbo的SPI机制分析5-Activate详解

private void loadClass(Map> extensionClasses, java.net.URL resourceURL,                                                      Class clazz, String name) throws NoSuchMethodException {    // clazz必须是type类型的,否则抛异常    if (!type.isAssignableFrom(clazz)) {    }    // 判断clazz是否为标注了@Adaptive注解,标注1    if (clazz.isAnnotationPresent(Adaptive.class)) {        if (cachedAdaptiveClass == null) {            cachedAdaptiveClass = clazz;        } else if (!cachedAdaptiveClass.equals(clazz)) {            // 抛异常,不能有多个标注有@Adaptive注解的类        }    }    // 判断是否是Wrapper类型,标注2    else if (isWrapperClass(clazz)) {        Set> wrappers = cachedWrapperClasses;        if (wrappers == null) {            cachedWrapperClasses = new ConcurrentHashSet>();            wrappers = cachedWrapperClasses;        }        wrappers.add(clazz);    }    // 程序进入此分支,表明clazz是一个普通的拓展类,Robot就是一个普通的拓展类    else {        // 检测clazz是否有默认的构造方法,如果没有,则抛出异常        clazz.getConstructor();        if (name == null || name.length() == 0) {            name = findAnnotationName(clazz);            if (name.length() == 0) {               // 抛异常            }        }        String[] names = NAME_SEPARATOR.split(name);        if (names != null && names.length > 0) {            // 用于@Activate根据条件激活,标注3            Activate activate = clazz.getAnnotation(Activate.class);            if (activate != null) {                cachedActivates.put(names[0], activate);            }            for (String n : names) {                if (!cachedNames.containsKey(clazz)) {                    cachedNames.put(clazz, n);                }                Class c = extensionClasses.get(n);                if (c == null) {                    // 存储名称到class的映射关系,这样就解析好了一行                    extensionClasses.put(n, clazz);                } else if (c != clazz) {                    // 抛异常                }            }        }    }}

感谢各位的阅读,以上就是"Dubbo的SPI机制介绍以及SPI加载class的方法"的内容了,经过本文的学习后,相信大家对Dubbo的SPI机制介绍以及SPI加载class的方法这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

0