我是靠谱客的博主 俊逸金针菇,最近开发中收集的这篇文章主要介绍dubbo之SPI分析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

写在前面

我们知道在jdk中提供了SPI 的机制,但是存在一个问题,就是加载的类会在加载时就进行初始化,而不是使用时再进行初始化,此时如果是初始化的类最终并没有使用,很明显这就是一种资源浪费了,对于dubbo,其提供了非常多的扩展点,并且每个扩展点都提供了很多不同的实现,而这些不同的实现实际运行过程中只有其中一个或者几个才会用到,比如Protocol,有DubboProtocol,InjvmProtocol,RestProtocol等,因此为了解决jdk SPI加载就是初始化的问题,dubbo再jdk SPI基础上进行了改进,自己实现提供了dubbo SPI,并在其基础上增加了一些其他的新功能,下面我们就一起来来看下吧!

1:项目对应的模块

dubbo SPI的功能是在dubbo-common模块提供的,如下图:
在这里插入图片描述

其中的org.apache.dubbo.common.extension.ExtensionLoader 类是dubbo SPI的核心类,在使用时正是使用该类来动态加载接口实现类的,正常的使用过程如下,首先定义一个接口以及其实现类,假设如下:

package dongshi.daddy;

import com.alibaba.dubbo.common.extension.SPI;

@SPI
public interface Fallback {
    Object invoker();
}

package dongshi.daddy;

public class FallbackImpl implements Fallback {

    @Override
    public Object invoker() {
        return "dubbo调用被熔断";
    }
}

然后在classpath下的META-INF/dubbo(没有则创建),以接口全限定名为名称创建文件,这里是dongshi.daddy.Fallback,然后添加如下内容在文件中:

fallbackImpl=com.workflow.form.hystrix.fallback.FallbackImpl

其中是获取对应实现类的key是fallbackImpl,然后通过如下代码获取:

class FakeCls {
    void fakeMethod() {
        ExtensionLoader<Fallback> loader = ExtensionLoader.getExtensionLoader(Fallback.class);
        Fallback fallback = loader.getExtension(fallbackName);
    }   
}

下面我们就从ExtensionLoader类开会分析吧!

2:获得拓展配置

想要对拓展进行初始化必须首先获取拓展配置内容,首先看下如何获取拓展配置。

2.1:getExtensionClass

源码如下:

class FakeCls {
    private Class<?> getExtensionClass(String name) {
        // 2022-01-13 17:38:30
        if (type == null)
            throw new IllegalArgumentException("Extension type == null");
        if (name == null)
            throw new IllegalArgumentException("Extension name == null");
        // 2022-01-13 17:41:26
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null)
            throw new IllegalStateException("No such extension "" + name + "" for " + type.getName() + "!");
        return clazz;
    }
}

2022-01-13 17:38:30处是判断name和type不为空,比如配置javassist=com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory,此时name值就是javassist,type值就是com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory
2022-01-13 17:41:26getExtensionClasses()是获取所有的扩展类,具体参考2.2:getExtensionClasses,因为返回的结构是一个字典Map<String, Class<?>>,所以直接调用get(name)获取我们需要的扩展类对应的Class对象。

2.2:getExtensionClasses

源码如下:

class FakeCls {
    private Map<String, Class<?>> getExtensionClasses() {
        // 先从缓存中获取,使用缓存的目的是为了提高性能
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                // 2022-01-13 17:51:37
                classes = cachedClasses.get();
                if (classes == null) {
                    // 2022-01-13 18:17:31
                    classes = loadExtensionClasses();
                    // 2022-01-13 18:17:36
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }
}

2022-01-13 17:51:37处是DCL双重锁检查,因为如果一个线程被阻塞后的一段时间获取到锁,此时cachedClasses可能已经初始化完毕了,即不再为null,如果不再次进行检查的话就会造成重复的初始化问题,关于DCL的应用可以参考文章基于DCL的单例懒汉模式实现 。2022-01-13 18:17:31处是加载扩展类,具体参考2.3:loadExtensionClasses2022-01-13 18:17:36处是将加载的类放到缓存中,定义为private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();,Holder类定义如下:

public class Holder<T> {

    private volatile T value;

    public void set(T value) {
        this.value = value;
    }

    public T get() {
        return value;
    }
}

可以看到只是进行了基于泛型的简单定义,来封装一个T类型的value。

2.3:loadExtensionClasses

源码如下:

class FakeCls {
    private Map<String, Class<?>> loadExtensionClasses() {
        // 获取SPI注解,获取默认的扩展类实现类名称,通过value配置
        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);
                // 只能配置一个默认实现类,否则抛出java.lang.IllegalStateException异常
                if (names.length > 1) {
                    throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
                            + ": " + Arrays.toString(names));
                }
                // 只配置了一个,则取第一个作为默认实现类,设置到缓存private String cachedDefaultName;
                // 注意这里是对象属性,并且每个扩展类实现都会使用一个ExtensionLoader封装,即二者是一对一的关系,所以可以这样子设置
                if (names.length == 1) cachedDefaultName = names[0];
            }
        }
        // 从服务配置目录加载扩展类们
        Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
        // 2022-01-14 09:58:24
        // 从指定目录中加载扩展类,注意优先级为
        // DUBBO_INTERNAL_DIRECTORY(META-INF/dubbo/internal/)
        // ->DUBBO_DIRECTORY(META-INF/dubbo/)
        // ->SERVICES_DIRECTORY(META-INF/services/)
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
        loadDirectory(extensionClasses, DUBBO_DIRECTORY);
        loadDirectory(extensionClasses, SERVICES_DIRECTORY);
        return extensionClasses;
    }
}

2022-01-14 09:58:24分别从private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";,private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";private static final String SERVICES_DIRECTORY = "META-INF/services/";目录下加载扩展类们,这里需要注意加载顺序,优先级如下:

META-INF/dubbo/internal:dubbo内部使用的配置目录。
META-INF/dubbo:外部扩展使用的配置目录。
META-INF/services:jdkSPI配置目录,主要是为了兼容jdk SPI。

META-INF/dubbo/internal如下图:
在这里插入图片描述

META-INF/dubbo因为是给外部扩展使用的,所以再源码中并没有提供任何配置,META-INF/services是兼容jdkSPI的,所以也没有提供任何配置。另外关于loadDirectory方法具体参考2.4:loadDirectory

2.4:loadDirectory

从扩展目录中加载扩展实现类们,源码如下:

class FakeCls {
    private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) {
        // 带有路径的完整文件名称,如META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Filter
        String fileName = dir + type.getName();
        try {
            Enumeration<java.net.URL> urls;
            // 获取加载当前类的类加载器
            ClassLoader classLoader = findClassLoader();
            // 可以认为不为null
            if (classLoader != null) {
                // 从classpath下加载指定文件,也会从jar包中加载
                urls = classLoader.getResources(fileName);
            } else {
                urls = ClassLoader.getSystemResources(fileName);
            }
            if (urls != null) {
                // 依次处理每一个
                while (urls.hasMoreElements()) {
                    // 如:jar:file:/D:/program_files/mvn_rep/com/alibaba/dubbo/2.6.6/dubbo-2.6.6.jar!/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Filter
                    java.net.URL resourceURL = urls.nextElement();
                    // 从指定的路径加载资源
                    // 2022-01-14 10:32:12
                    loadResource(extensionClasses, classLoader, resourceURL);
                }
            }
        } catch (Throwable t) {
            logger.error("Exception when load extension class(interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }
}

2022-01-14 10:32:12处是从指定的URL路径加载资源,具体参考2.4.1:loadResource

2.4.1:loadResource

源码如下:

class FakeCls {
    private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
        try {
            // 通过java.net.URL#openStream获取流,并开始读取
            BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
            try {
                String line;
                while ((line = reader.readLine()) != null) {
                    // 以为#后代表注释内容,所以需要过滤掉,比如mock=com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper # 模拟类
                    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('=');
                            // 分别获取名称和值,如cache=com.alibaba.dubbo.cache.filter.CacheFilter
                            // name:如cache line:com.alibaba.dubbo.cache.filter.CacheFilter
                            if (i > 0) {
                                name = line.substring(0, i).trim();
                                line = line.substring(i + 1).trim();
                            }
                            if (line.length() > 0) {
                                // 2022-01-14 13:29:10
                                loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
                            }
                        } catch (Throwable t) {
                            IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
                            exceptions.put(line, e);
                        }
                    }
                }
            } finally {
                // 关闭流,也可考虑try with resource语法糖方式来做
                reader.close();
            }
        } catch (Throwable t) {
            logger.error("Exception when load extension class(interface: " +
                    type + ", class file: " + resourceURL + ") in " + resourceURL, t);
        }
    }
}

2022-01-14 13:29:10处是加载类,具体参考2.4.2:loadResource

2.4.2:loadResource

源码如下:

class FakeCls {
    private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
        // clazz必须是type的子类,不然抛出java.lang.IllegalStateException
        if (!type.isAssignableFrom(clazz)) {
            throw new IllegalStateException("Error when load extension class(interface: " +
                    type + ", class line: " + clazz.getName() + "), class "
                    + clazz.getName() + "is not subtype of interface.");
        }
        // 2022-01-16 17:13:10
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            // 因为当前类是标注了@Adaptive注解的类,后续在生成动态子类时需要用到(通过getAdaptiveExtension方法可获取到)
            // 为了不需要重复加载,所以这里直接设置到缓存变量中,后续便可直接使用,提高性能
            if (cachedAdaptiveClass == null) {
                cachedAdaptiveClass = clazz;
            // 如果时缓存的Adaptive注解clz已经有值,并且和当前的不相等,则说明有多个扩展类都使用了@Adaptive注解,这种情况
            // 不被允许,会抛出java.lang.IllegalStateException
            } else if (!cachedAdaptiveClass.equals(clazz)) {
                throw new IllegalStateException("More than 1 adaptive class found: "
                        + cachedAdaptiveClass.getClass().getName()
                        + ", " + clazz.getClass().getName());
            }
        // 2022-01-17 10:40:07 
        } else if (isWrapperClass(clazz)) {
            // 保存到集合中
            Set<Class<?>> wrappers = cachedWrapperClasses;
            if (wrappers == null) {
                cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
                wrappers = cachedWrapperClasses;
            }
            wrappers.add(clazz);
        // 就是普通的扩展类,即没有使用@Adaptive注解,也没有一个使用接口作为唯一参数的构造函数
        } else {
            // 这行代码是要保证有一个无参数的构造函数,即默认构造函数,因为后续需要用到创建对象
            clazz.getConstructor();
            // 2022-01-17 13:15:19
            if (name == null || name.length() == 0) {
                name = findAnnotationName(clazz);
                // 应该不会有这种情况吧!!!
                if (name.length() == 0) {
                    throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
                }
            }
            // 扩展名可能是逗号分隔的多个,如concreteUseInSubClsInterface2,concreteUseInSubClsInterface21=dongshi.daddy.adaptive.useinsubcls.ConcreteUseInSubClsInterface2
            String[] names = NAME_SEPARATOR.split(name);
            if (names != null && names.length > 0) {
                // 2022-01-17 16:43:36
                Activate activate = clazz.getAnnotation(Activate.class);
                if (activate != null) {
                    // private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
                    cachedActivates.put(names[0], activate);
                }
                for (String n : names) {
                    // private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
                    // 扩展类class->扩展类名字,只保存第一个name
                    if (!cachedNames.containsKey(clazz)) {
                        cachedNames.put(clazz, n);
                    }
                    // Map<String, Class<?>> extensionClasses,扩展类名称->扩展类class
                    // 如果是当前扩展类名称不包含在结果中,则put
                    Class<?> c = extensionClasses.get(n);
                    if (c == null) {
                        extensionClasses.put(n, clazz);
                    // 不同的扩展类使用了相同的扩展类名称,则抛出java.lang.IllegalStateException
                    } else if (c != clazz) {
                        throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
                    }
                }
            }
        }
    }
}

2022-01-16 17:13:10处是处理@Adaptive ,该注解用来实现根据用户设置的参数动态选择具体的扩展类。2022-01-17 10:40:07处是判断是否存在一个使用当前type作为唯一参数的构造函数,源码如下:

class FakeCls {
    private boolean isWrapperClass(Class<?> clazz) {
        // 存在一个使用当前type为唯一参数的构造函数的话则返回true,否则返回false
        try {
            clazz.getConstructor(type);
            return true;
        } catch (NoSuchMethodException e) {
            return false;
        }
    }
}

2022-01-17 13:15:19处是处理在SPI配置文件中没有设置name的情况时,通过findAnnotationName方法获取名称,如下的第二行就是这种情况:

concreteUseInSubClsInterface2=dongshi.daddy.adaptive.useinsubcls.ConcreteUseInSubClsInterface2
dongshi.daddy.adaptive.useinsubcls.ConcreteUseInSubClsInterface3

源码如下:

class FakeCls {
    private String findAnnotationName(Class<?> clazz) {
        // 获取类上使用的@Extension注解,并使用其value作为name结果
        com.alibaba.dubbo.common.Extension extension = clazz.getAnnotation(com.alibaba.dubbo.common.Extension.class);
        // 如果是没有配置@Extension注解
        if (extension == null) {
            // 获取类的简单名称
            String name = clazz.getSimpleName();
            // 如果是类的简单名称是以接口的简单名称结尾,则去除接口的简单名称后缀,如接口简单名称是UseInSubClsInterface,扩展类名称是Concrete4UseInSubClsInterface
            // 则之类结果就是Concrete4
            if (name.endsWith(type.getSimpleName())) {
                name = name.substring(0, name.length() - type.getSimpleName().length());
            }
            // 转小写
            return name.toLowerCase();
        }
        return extension.value();
    }

}

2022-01-17 16:43:36处是处理类上标记了@Activate注解的情况,具体参考5:@Activate。到这里,在DUBBO SPI配置文件中配置的信息,已经成功加载并转换为程序的相关变量进行存储,接下来看下如何获取ExtensionLoader对象,因为在程序中获取扩展类一般就是通过该对象完成的,具体参考4:获取扩展类加载器

4:获取扩展类加载器

在dubbo的代码里,经常看到如下的代码:

ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name)

就是通过扩展类加载器ExtensionLoader来获取目标扩展类,下面我们就从getExtensionLoader方法开始分析。

4.1:getExtensionLoader

源码如下:

class FakeCls {
    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 interface!");
        }
        // 必须有SPI注解
        /*
        private static <T> boolean withExtensionAnnotation(Class<T> type) {
            return type.isAnnotationPresent(SPI.class);
        }
        */
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type(" + type +
                    ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }
        // 从private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
        // 中获取,获取不到,则new一个
        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        if (loader == null) {
            // 2022-01-17 17:49:55
            // 不存在才put
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        return loader;
    }
}

2022-01-17 17:49:55处是在不存在指定类型的扩展类加载器时才put,关于ExtensionLoader构造函数具体参考4.1.1:ExtensionLoader构造函数

4.1.1:ExtensionLoader构造函数

源码如下:

class FakeCls {
    private ExtensionLoader(Class<?> type) {
        // 设置扩展类对应的接口class
        this.type = type;
        // 2022-01-18 10:59:55
        // 设置扩展工厂,实现类似于Spring IOC的功能,注意这里的3目,如果是不加这个判断,将会造成死循环
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }
}

2022-01-18 10:59:55处是设置扩展工厂,ExtensionFactory有3个子类,一个是@Adaptive 子类,另外两个扩展类是通过SPI和spring容器获取对象,如下:

在这里插入图片描述

使用上是在调用方法injectExtension(instance)注入实例的属性值,即通过ExtensionFactory获取对应属性的实例,并进行set。关于ExtensionFactory具体参考6:ExtensionFactory

4.2:getExtension

通过该方法获取扩展类的对应的对象实例,源码如下:

class FakeCls {
    // 通过提供的名称name获取扩展类
    @SuppressWarnings("unchecked")
    public T getExtension(String name) {
        // 检测name的合法性,不合法则抛出java.lang.IllegalArgumentException异常
        if (name == null || name.length() == 0)
            throw new IllegalArgumentException("Extension name == null");
        // 2022-01-19 10:15:00
        // name为true则获取默认扩展类
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        // 从缓存中获取,可以认为为null
        Holder<Object> holder = cachedInstances.get(name);
        // 这个判断只是提前创建一个用于存放对应name的Holder
        if (holder == null) {
            // 执行putIfAbsent的原因是因为考虑并发的场景
            cachedInstances.putIfAbsent(name, new Holder<Object>());
            holder = cachedInstances.get(name);
        }
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                // 2022-01-19 10:34:01
                instance = holder.get();
                if (instance == null) {
                    // 2022-01-19 10:39:50
                    instance = createExtension(name);
                    // 设置到holder
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }
}

2022-01-19 10:15:00处是当name=true时获取默认的扩展类,具体参考4.2.1:getDefaultExtension2022-01-19 10:34:01DCL ,防止获取锁时进入同步代码块的条件已经不再满足。2022-01-19 10:39:50处是创建扩展类,具体参考4.2.2:createExtension

4.2.1:getDefaultExtension

源码如下:

class FakeCls {
    public T getDefaultExtension() {
        // 加载扩展类
        getExtensionClasses();
        // 如果cachedDefaultName没有,则renturn null,其中cachedDefaultName值就是在接口的@SPI注解上配置的value
        // 当且仅当配置了一个值时才会被设置到cachedDefaultName中,如@SPI("apple"),apple就是默认值,如@SPI("apple,banana"),则不会设置默认值
        // 另外需要注意判断"true".equals(cachedDefaultName),即默认值不能为true,如果是没有这个判断会发生当设置@SPI("true"),又执行getExtension("true")时。就会发生死循环,因为会发生无限递归调用
        if (null == cachedDefaultName || cachedDefaultName.length() == 0
                || "true".equals(cachedDefaultName)) {
            return null;
        }
        // 通过默认值获取扩展类
        return getExtension(cachedDefaultName);
    }
}

4.2.2:createExtension

源码如下:

class FakeCls {
    private T createExtension(String name) {
        // 获取对应的Class对象
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
            // 缓存获取,可以认为为null
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                // 直接反射创建实例
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            // 2022-01-19 10:55:16
            // 注入属性,即有setter的类变量
            injectExtension(instance);
            // 存在wrapper class,即有一个使用当前type作为唯一参数的构造函数的类
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            // 如果有,则创建wrapper对应的实例
            if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
                // 循环创建wrapper class实例,并调用方法injectExtension注入其属性值
                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);
        }
    }
}

2022-01-19 10:55:16处是注入属性,具体参考4.2.3:injectExtension

4.2.3:injectExtension

源码如下:

class FakeCls {
    private T injectExtension(T instance) {
        try {
            // 2022-01-19 12:29:04
            if (objectFactory != null) {
                // 获取当前实例的所有方法
                for (Method method : instance.getClass().getMethods()) {
                    // 寻找方法签名如public void setXxxx(Xxx xxx) {} 的方法
                    if (method.getName().startsWith("set")
                            && method.getParameterTypes().length == 1
                            && Modifier.isPublic(method.getModifiers())) {
                        // 如果是属性使用了@DisableInject注解,则忽略不注入
                        if (method.getAnnotation(DisableInject.class) != null) {
                            continue;
                        }
                        // 获取参数类型
                        Class<?> pt = method.getParameterTypes()[0];
                        try {
                            // 获取属性名
                            String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                            // 通过getExtension方法获取要注入的对象,这里调用的是AdaptiveExtensionFactory#getExtension
                            Object object = objectFactory.getExtension(pt, property);
                            // 如果不为null,则反射调用
                            if (object != null) {
                                method.invoke(instance, object);
                            }
                        } catch (Exception e) {
                            logger.error("fail to inject via method " + method.getName()
                                    + " of interface " + type.getName() + ": " + e.getMessage(), e);
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return instance;
    }
}

2022-01-19 12:29:04处的objectFactory是ExtensionFactory的一个实例,具体参考6:ExtensionFactory。到这里,获取普通的扩展对象已经解析完毕了,接下来看下如果获取使用@Adaptive 注解的自适应扩展类,具体参考4.3:获得自适应的拓展对象

4.3:获得自适应的拓展对象

获取自适应扩展对象有专门的API方法getAdaptiveExtension,一般像ExtensionLoader.getExtensionLoader(UseInSubClsInterface.class).getAdaptiveExtension()这样使用,接下来一起看下这部分内容。

4.3.1:getAdaptiveExtension

源码如下:

class FakeCls {
    public T getAdaptiveExtension() {
        // 缓存获取,可以认为为null
        Object instance = cachedAdaptiveInstance.get();
        if (instance == null) {
            // 认为true
            if (createAdaptiveInstanceError == null) {
                // JVM级别同步
                synchronized (cachedAdaptiveInstance) {
                    // 2022-01-19 14:20:55
                    instance = cachedAdaptiveInstance.get();
                    if (instance == null) {
                        try {
                            // 2022-01-19 14:26:12
                            instance = createAdaptiveExtension();
                            // 添加到缓存
                            cachedAdaptiveInstance.set(instance);
                        } catch (Throwable t) {
                            createAdaptiveInstanceError = t;
                            throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
                        }
                    }
                }
            } else {
                throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
            }
        }
        return (T) instance;
    }
}

2022-01-19 14:20:55处是DCL 防止因为获取同步????后最初进入同步代码块的状态判断发生变更而导致程序错误。2022-01-19 14:26:12处是创建自适应拓展类实例,具体参考4.3.2:createAdaptiveExtension

4.3.2:createAdaptiveExtension

源码如下:

class FakeCls {
    private T createAdaptiveExtension() {
        try {
            // 2022-01-19 14:40:31
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }
}

2022-01-19 14:40:31处injectExtension是注入对象属性值,具体参考4.2.3:injectExtension,getAdaptiveExtensionClass是获取自适应扩展类的class对象,具体参考4.3.3:getAdaptiveExtensionClass

4.3.3:getAdaptiveExtensionClass

源码如下:

class FakeCls {
    private Class<?> getAdaptiveExtensionClass() {
        // 2022-01-19 15:03:32
        // 获取扩展类
        getExtensionClasses();
        // 有一种场景这里会不为null,就是将@Adaptive注解使用在扩展接口子类上的情况
        // 有两种场景这里会为null,@Adaptive使用在接口的方法上这里为null,根本就没有使用@Adaptive注解的时候为null
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        // 2022-01-19 15:07:19
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }
}

2022-01-19 15:03:32处参考2.2:getExtensionClasses2022-01-19 15:07:19处是将@Adaptive注解使用在接口的方法上的场景不考虑没有使用的场景,具体参考4.3.4:createAdaptiveExtensionClass

4.3.4:createAdaptiveExtensionClass

源码如下:

class FakeCls {
    private Class<?> createAdaptiveExtensionClass() {
        // 2022-01-19 15:49:11
        String code = createAdaptiveExtensionClassCode();
        // 以下是将动态生成的java代码,并最终生成对应的class,相当于经过了javac Xxx.java的过程
        ClassLoader classLoader = findClassLoader();
        com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        return compiler.compile(code, classLoader);
    }
}

2022-01-19 15:49:11处是生成接口的动态子类的类字符串,所作的事情就是根据url中的参数来动态获取扩展实现类并调用目标方法,如下是我本地的生成的结果:

package dongshi.daddy.adaptive;

import com.alibaba.dubbo.common.extension.ExtensionLoader;

// 实现扩展类接口,即是扩展类接口的一个子类,FruitGranter接口定义如下:
/*
@SPI("apple")
public interface FruitGranter {

  Fruit grant();

//  @Adaptive
  @Adaptive({ "find.fruit.extenstion" })
  String watering(URL url);
}
*/
public class FruitGranter$Adaptive implements dongshi.daddy.adaptive.FruitGranter {
    // 实现watering方法,该方法在接口我们标注了@Adaptive注解,因此会对该方法动态生成逻辑
    public java.lang.String watering(com.alibaba.dubbo.common.URL arg0) {
        // 必须有URL参数,强制要求,因为要从URL上提取参数,获取扩展类
        if (arg0 == null) throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg0;
        // 通过参数find.fruit.extenstion获取扩展类名称,默认是apple
        String extName = url.getParameter("find.fruit.extenstion", "apple");
        // 没有获取到扩展类的名称,则抛出运行时异常java.lang.IllegalStateException
        if (extName == null)
            throw new IllegalStateException("Fail to get extension(dongshi.daddy.adaptive.FruitGranter) name from url(" + url.toString() + ") use keys([find.fruit.extenstion])");
        // 根据扩展类名称获取扩展类实例
        // getExtensionLoader参考:4.1:getExtensionLoader
        // getExtension(extName)参考:4.2:getExtension
        dongshi.daddy.adaptive.FruitGranter extension = (dongshi.daddy.adaptive.FruitGranter) ExtensionLoader.getExtensionLoader(dongshi.daddy.adaptive.FruitGranter.class).getExtension(extName);
        // 调用目标扩展类的watering方法
        return extension.watering(arg0);
    }
    
    // 实现grant方法,因为该方法在接口中没有标注@Adaptive注解,所以默认就是抛出java.lang.UnsupportedException
    public dongshi.daddy.adaptive.Fruit grant() {
        throw new UnsupportedOperationException("method public abstract dongshi.daddy.adaptive.Fruit dongshi.daddy.adaptive.FruitGranter.grant() of interface dongshi.daddy.adaptive.FruitGranter is not adaptive method!");
    }
}

到这里自适应拓展类获取的相关源码就分析完毕了,还省最后一个,即使用了@Activate注解的类,关于该注解参考5:@Activate,获取激活扩展类是通过方法getActivateExtension,具体参考7:获得激活的拓展对象数组

5:@Activate

该注解用来根据指定的条件激活扩展类,具体看下源码:

// 根据指定的条件来自动激活扩展类,这些条件可以是:
// 1:当前是服务提供者端,还是服务消费者端,对应的方法是String[] group() default {};
// 2:在URL上是否包含指定的key,对应的方法是String[] value() default {};
// 可以调用方法ExtensionLoader#getActivateExtension(URL, String, String)获取指定条件下可以被激活的扩展类
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
    // 激活的组,即服务提供者端,服务消费者端,对应的值是public static final String PROVIDER = "provider"; public static final String CONSUMER = "consumer";
    // 在调用方法ExtensionLoader#getActivateExtension(URL, String, String)时会被用来进行匹配
    String[] group() default {};

    // 当在URL上有指定的参数key时激活当前的扩展类。例如,当前值配置的是@Activate("cache, validation"),只有在URL中包含key cache或者是validation时
    // 该扩展类才会被激活
    String[] value() default {};

    // 需要放在当前扩展类之前的扩展类集合,可忽略
    String[] before() default {};

    // 需要放在当前扩展类之后的扩展类集合,可忽略
    String[] after() default {};

    // 排序号,多个扩展类,并且需要排序时使用
    int order() default 0;
}

6:ExtensionFactory

这是一个用于获取扩展类的工厂,接口如下:

@SPI
public interface ExtensionFactory {
    // 获取指定类型的指定名称的扩展类
    <T> T getExtension(Class<T> type, String name);
}

该类有3个子类,类图如下:

在这里插入图片描述

6.1:AdaptiveExtensionFactory

这是使用了@Adaptive 注解的自适应拓展类实现类,源码如下:

@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
    // 存储其他的非自适应扩展实现类,目前配置如下:
    /*
    adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
    spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
    spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory
    */
    // 其中名称为adaptive的是本类,不会获取添加到集合中
    private final List<ExtensionFactory> factories;

    public AdaptiveExtensionFactory() {
        ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
        for (String name : loader.getSupportedExtensions()) {
            // 注意这里调用的是getExtension方法,不会获取本类,想要获取本类的话需要调用getAdaptiveExtension
            list.add(loader.getExtension(name));
        }
        // 调用方法Collections#unmodifiableList转换为不可修改集合
        factories = Collections.unmodifiableList(list);
    }

    // 获取扩展类的方法
    @Override
    public <T> T getExtension(Class<T> type, String name) {
        // 依次从接口的非自适应扩展类子类中获取,没有自己实现的话就是通过SPI,spring容器获取
        for (ExtensionFactory factory : factories) {
            T extension = factory.getExtension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

}

6.2:SpiExtensionFactory

需要注意:仅支持@Adaptive自适应扩展子类的注入

源码如下:

public class SpiExtensionFactory implements ExtensionFactory {
    @Override
    public <T> T getExtension(Class<T> type, String name) {
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
            if (!loader.getSupportedExtensions().isEmpty()) {
                // 返回对应type接口的adaptive自适应扩展类,因此仅仅支持存在adaptive子类的接口的自动注入
                return loader.getAdaptiveExtension();
            }
        }
        return null;
    }
}

6.3:SpringExtensionFactory

该类支持依赖于spring的IOC容器ApplicationContext完成属性注入,源码如下:

public class SpringExtensionFactory implements ExtensionFactory {
    private static final Logger logger = LoggerFactory.getLogger(SpringExtensionFactory.class);
    // 查找目标对象的容器集合,通过调用方法addApplicationContext添加
    private static final Set<ApplicationContext> contexts = new ConcurrentHashSet<ApplicationContext>();
    
    private static final ApplicationListener shutdownHookListener = new ShutdownHookListener();
    
    // 添加spring容器ApplicationContext
    public static void addApplicationContext(ApplicationContext context) {
        contexts.add(context);
        BeanFactoryUtils.addApplicationListener(context, shutdownHookListener);
    }
    
    // 移除spring容器ApplicationContext
    public static void removeApplicationContext(ApplicationContext context) {
        contexts.remove(context);
    }
    
    // 获取当前的spring容器
    public static Set<ApplicationContext> getContexts() {
        return contexts;
    }

    // 清空spring容器集合
    public static void clearContexts() {
        contexts.clear();
    }
    @Override
    @SuppressWarnings("unchecked")
    public <T> T getExtension(Class<T> type, String name) {
        // 遍历所有的spring容器集合
        for (ApplicationContext context : contexts) {
            // 包含则调用getBean获取
            if (context.containsBean(name)) {
                Object bean = context.getBean(name);
                // 如果是type类型的,即当前接口的子类,则强转返回
                if (type.isInstance(bean)) {
                    return (T) bean;
                }
            }
        }
        logger.warn("No spring extension (bean) named:" + name + ", try to find an extension (bean) of type " + type.getName());
        if (Object.class == type) {
            return null;
        }
        // 执行到这里,说明是byName没有获取到,则通过byType的方式尝试获取
        // 关于几种注入方式可以参考文章:https://blog.csdn.net/wang0907/article/details/115271264
        for (ApplicationContext context : contexts) {
            try {
                return context.getBean(type);
            } catch (NoUniqueBeanDefinitionException multiBeanExe) {
                logger.warn("Find more than 1 spring extensions (beans) of type " + type.getName() + ", will stop auto injection. Please make sure you have specified the concrete parameter type and there's only one extension of that type.");
            } catch (NoSuchBeanDefinitionException noBeanExe) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Error when get spring extension(bean) for type:" + type.getName(), noBeanExe);
                }
            }
        }
        logger.warn("No spring extension (bean) named:" + name + ", type:" + type.getName() + " found, stop get bean.");
        return null;
    }

    private static class ShutdownHookListener implements ApplicationListener {
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            if (event instanceof ContextClosedEvent) {
                DubboShutdownHook shutdownHook = DubboShutdownHook.getDubboShutdownHook();
                shutdownHook.destroyAll();
            }
        }
    }
}

7:获得激活的拓展对象数组

在程序中想要获取激活的扩展对象数组,一般是执行代码ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group),下面我们就从getActivateExtension方法开始分析。

7.1:getActivateExtension

源码如下:

class FakeCls {
    public List<T> getActivateExtension(URL url, String key, String group) {
        // 从url上获取参数值,即要激活的扩展类名称
        String value = url.getParameter(key);
        // 2022-01-19 16:31:26
        // 获取符合自动激活条件的扩展类集合
        return getActivateExtension(url, value == null || value.length() == 0 ? null : Constants.COMMA_SPLIT_PATTERN.split(value), group);
    }
}

2022-01-19 16:31:26处源码如下:

class FakeCls {
    public List<T> getActivateExtension(URL url, String[] values, String group) {
        // 结果集合
        List<T> exts = new ArrayList<T>();
        // 获取要激活的扩展类的名称集合
        List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
        // 如果是不包含"-default"
        if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
            // 获取加载类classes
            getExtensionClasses();
            for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
                // 扩展类名称
                String name = entry.getKey();
                // 扩展类@Activate注解
                Activate activate = entry.getValue();
                // group:consumer/provider,
                // activate.group():注解配置的激活的group
                if (isMatchGroup(group, activate.group())) {
                    // 获取对应的扩展类,为什么先get,而不是在if内部get?
                    T ext = getExtension(name);
                    if (!names.contains(name) // names是自定义的,即过滤自定义的
                            && !names.contains(Constants.REMOVE_VALUE_PREFIX + name) // 不包含"-xxx",即显示排除
                            && isActive(activate, url)) { // 必须是激活的,即@Activate.value配置的值在URL中存在key且不为空("", false,null,0,N/A)
                        exts.add(ext);
                    }
                }
            }
            Collections.sort(exts, ActivateComparator.COMPARATOR);
        }
        // 用户自定义的
        List<T> usrs = new ArrayList<T>();
        for (int i = 0; i < names.size(); i++) {
            String name = names.get(i);
            // 不以“-”开头,并且不包含"-xxx",即排除当前name
            if (!name.startsWith(Constants.REMOVE_VALUE_PREFIX)
                    && !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {
                // 如果时“name=default”
                if (Constants.DEFAULT_KEY.equals(name)) {
                    if (!usrs.isEmpty()) {
                        // 将urs添加到exts的最前面
                        exts.addAll(0, usrs);
                        // 因为已经添加所以clear
                        usrs.clear();
                    }
                } else {
                    // 获取并添加
                    T ext = getExtension(name);
                    usrs.add(ext);
                }
            }
        }
        // 自定义的集合添加到系统的集合
        if (!usrs.isEmpty()) {
            exts.addAll(usrs);
        }
        return exts;
    }
}

最后

以上就是俊逸金针菇为你收集整理的dubbo之SPI分析的全部内容,希望文章能够帮你解决dubbo之SPI分析所遇到的程序开发问题。

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

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

评论列表共有 0 条评论

立即
投稿
返回
顶部