Dubbo源码之spi

155 阅读7分钟

版本 2.7.4.1

spi是什么

来自百度:SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。

java spi

简单的代码示例

public interface People {
    String getName();
}
public class Student implements People {
    
    @Override
    public String getName() {
        System.out.println("student");
        return "Student";
    }
}

在META-INF/services/ 路径下面配置实现类

测试类:

    public static void main(String[] args) {
        ServiceLoader<People> serviceLoader = ServiceLoader.load(People.class);
        Iterator<People> iterator = serviceLoader.iterator();
        // resources/META-INF/services/ 源码已经写死了 META-INF/services/ 这个路径
        //所以只能在这个路径下面编写接口实现类
        while (iterator.hasNext()){
            //获取接口实现类,做相应的操作
            People next = iterator.next();
            next.getName();
        }
    }

以上就是java spi的简单使用,可以实现对某个接口的动态扩展。这也是为什么要学习dubbo的spi,我们也可以对dubbo进行动态扩展。

dubbo的spi

dubbo的spi并不是使用原生的java spi机制,dubbo的spi机制更加丰富

入口

ServiceConfig里面的一句代码开始

//通过 ExtensionLoader 获取 Protocol 自适应扩展点
    private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

一步一步分析,先看 ExtensionLoader.getExtensionLoader(Protocol.class)

    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    //从map中拿 ExtensionLoader ,第一次没拿到
        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        if (loader == null) {
        //没拿到就new 一个 ExtensionLoader 这个动作又做了好多事情,下面说
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        return loader;
    }

new ExtensionLoader<T>(type) 做了什么

    private ExtensionLoader(Class<?> type) {
        this.type = type;
        //三目运算 这时候的 type=Protocol.class 
        //所以走的 ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()
        //发现又回到 ExtensionLoader.getExtensionLoader(ExtensionFactory.class) 这个代码里面
        //又掉绕这里, ExtensionLoader.getExtensionLoader(ExtensionFactory.class)
        //这句代码走到这里的时候type=ExtensionFactory 所以直接返回null
        //当type=Protocol.class 的时候,objectFactory 是 ExtensionFactory 一个扩展点
        //这个后面说
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

所以ExtensionLoader.getExtensionLoader(Protocol.class) 这个代码做的事情就是new一个 ExtensionLoader 对象,然后把type=Protocol.class放到 ExtensionLoader的type属性,同时通过spi机制获取到ExtensionFactory的扩展点放到objectFactory 这个属性。这两个属性都是为后面的 getAdaptiveExtension 方法服务的

``getAdaptiveExtension 做了什么,顾名思义,这个方法就是获取某个接口的自适应扩展点 dubbo的自适应扩展点是什么?下面源码有答案

public T getAdaptiveExtension() {
//cachedAdaptiveInstance 是缓存自适应扩展点
        Object instance = cachedAdaptiveInstance.get();
        //一开始缓存肯定为空
        if (instance == null) {
        //dubbo大量用到双重检查
            synchronized (cachedAdaptiveInstance) {
                instance = cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
                    //缓存拿不到,看是真正创建自适应扩展点
                    //重点代码,下面解析
                        instance = createAdaptiveExtension();
                    //放到缓存里面
                        cachedAdaptiveInstance.set(instance);
                    } 
                }
            }
        }

        return (T) instance;
    }

createAdaptiveExtension 获取自适应扩展点

    private T createAdaptiveExtension() {
        try {
        //一行代码做了很多事情
        //1.先看 getAdaptiveExtensionClass 方法
        //2.newInstance 这个不用看,就是通过反射new一个对象
        //3.最后看 injectExtension 这个方法
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        }
    }

getAdaptiveExtensionClass 获取自适应扩展点的class


    private Class<?> getAdaptiveExtensionClass() {
    //重点方法,去加载type这个接口下面所有的扩展点,放到缓存中
        getExtensionClasses();
    //如果上面的逻辑已经加载了自适应扩展点,就直接返回自适应扩展点的class    
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
    //拼接代码生成自适应扩展点,然后默认使用  javassist  生成class    
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

getExtensionClasses

    private Map<String, Class<?>> getExtensionClasses() {
    //cachedClasses 缓存type下面的实现类
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                //开始去load实现类
                    classes = loadExtensionClasses();
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }

loadExtensionClasses 加载实现类

    private Map<String, Class<?>> loadExtensionClasses() {
        cacheDefaultExtensionName();
    //存放实现类的容器
        Map<String, Class<?>> extensionClasses = new HashMap<>();
    // 有两个方法,另外一个应该是为了兼容alibaba相关的类,往下看loadDirectory方法   
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        //.....省略去加载 "META-INF/services/""META-INF/dubbo/" 下面的实现类
        //因为都是调用 loadDirectory 方法加载的
        return extensionClasses;
    }

loadDirectory 加载文件

 private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
        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();
                    //load文件下来之后开始解析
                    loadResource(extensionClasses, classLoader, resourceURL);
                }
            }
        } 
    }

loadResource 解析实现类文件


    private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
      //...省略大量逻辑,大概就是从文件加载出实现类
      //这个文件跟java的spi文件不一样的是用键值对来表示
      
      //加载文件,下面详解
      
     loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
/........
    }

loadClass 加载类


    private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
//判断是否加 @Adaptive 如果是就放到 cachedAdaptiveClass 这个属性里
//所以在实现类中 加@Adaptive 注解就是自适应扩展点,自适应扩展点只能有一个
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            cacheAdaptiveClass(clazz);
//判断是否包装类,通过 clazz.getConstructor(type); 这行代码判断
//如果不抛出异常,表示是包装类,否则不是
//包装类会缓存到 cachedWrapperClasses,后面会用到
        } else if (isWrapperClass(clazz)) {
            cacheWrapperClass(clazz);
        } else {
          //...省略名称获取
            if (ArrayUtils.isNotEmpty(names)) {
            //将 有@Activate 注解的类缓存到 cachedActivates 中
                cacheActivateClass(clazz, names[0]);
                for (String n : names) {
            //cachedNames 缓存类跟名称的键值
                    cacheName(clazz, n);
            //extensionClasses 缓存名称跟类的键值
                    saveInExtensionClass(extensionClasses, clazz, n);
                }
            }
        }
    }

loadClass 这个方法可以看出来,spi的扩展点有很多形式,比如加@Adaptive @Active 包装类,普通类 但是 cachedClasses 这个只放普通和@Active@Adaptive 放到 cachedAdaptiveClass 包装类放到 cachedWrapperClasses@Active还会放到 cachedActivates

现在回到 getAdaptiveExtensionClass 这个方法

    private Class<?> getAdaptiveExtensionClass() {
    //上面分析到这里,从配置文件中加载所有的实现类出来,放到本地缓存
        getExtensionClasses();
    //这个是缓存@Adaptive     
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
    //如果所有实现类没有一个注有 @Adaptive 就走下面的逻辑,往下走    
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }
    private Class<?> createAdaptiveExtensionClass() {
    //这个方法很长很复杂,不往下走了,总的就是判断type是否注有@Adaptive,没有就会抛异常
    //有就会为这个方法生成具体的实现
        String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
        ClassLoader classLoader = findClassLoader();
    //生成code之后,默认用javassist 生成class    
        org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        return compiler.compile(code, classLoader);
    }

以上就差不多完成从配置文件加载到本地缓存的过程,接下来看 injectExtension 方法做了什么 injectExtension 实现了dubbo的ioc的注入


    private T injectExtension(T instance) {
//这个如果还记得的话,就是上文中实例ExtensionLoader的时候,new进去的
//所以这时候不为空
        if (objectFactory == null) {
            return instance;
        }
        try {
        //遍历所有的方法
            for (Method method : instance.getClass().getMethods()) {
        //如果不是set方法就不做操作    
                if (!isSetter(method)) {
                    continue;
                }
              //如果有 @DisableInject 在方法上,说明不需要自动注入
                if (method.getAnnotation(DisableInject.class) != null) {
                    continue;
                }
                //获取需要注入得calss
                Class<?> pt = method.getParameterTypes()[0];
                if (ReflectUtils.isPrimitives(pt)) {
                    continue;
                }

                try {
                //获取需要注入的名称比如setXX 就是通过xx去容器获取
                    String property = getSetterProperty(method);
                //objectFactory 的作用来了,就是从容器中获取依赖,下面讲    
                    Object object = objectFactory.getExtension(pt, property);
                    if (object != null) {
                //调用反射,注入依赖    
                        method.invoke(instance, object);
                    }
                } 
            }
        } 
        return instance;
    }

完成注入之后就可以返回对象了,接下来看 objectFactory.getExtension 是怎么从容器中获取依赖的 在AdaptiveExtensionFactory中实现。

public <T> T getExtension(Class<T> type, String name) {
//AdaptiveExtensionFactory 实例化的时候会通过spi机制加载
//ExtensionFactory 的普通实现类,目前包括
//SpiExtensionFactory 和 SpringExtensionFactory
//然后遍历 ExtensionFactory 实现类,执行 getExtension 获取依赖
//接下来分别看看这两个实现类的逻辑
        for (ExtensionFactory factory : factories) {
            T extension = factory.getExtension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

SpringExtensionFactory 从spring容器中获取依赖

 public <T> T getExtension(Class<T> type, String name) {
//......省略一下判断
//从spring上下文判断bean存不存在,如果存在getBean返回
        for (ApplicationContext context : CONTEXTS) {
            if (context.containsBean(name)) {
                Object bean = context.getBean(name);
                if (type.isInstance(bean)) {
                    return (T) bean;
                }
            }
        }
//.....
    }

SpiExtensionFactory 通过dubbo的spi机制获取依赖,需要的依赖可能通过spi机制加载

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()) {
            //获取自适应扩展点
                return loader.getAdaptiveExtension();
            }
        }
        return null;
    }

到这里dubbo的spi已经理的差不多了

为什么需要了解dubbo的spi

1.方便在使用dubbo的时候对dubbo进行扩展 2.dubbo很多地方都会用到spi,所以想要了解dubbo就先要知道dubbo的spi干了什么

未完待续..................