dubbo spi 源码分析

初探java spi

初探java spi

java spi,dubbo spi

Java SPI的缺点:

  • 不支持按需加载,迭代器遍历会实例化所有的实现类,即使它没有被用到,太浪费资源了。
  • 获取实现类的方式不灵活,只能通过迭代器遍历。
  • 没有缓存,实现类会被多次创建。
  • 扩展加载失败,失败原因丢失,不方便排查。
  • 不支持AOP和IOC。

Dubbo SPI定义了一套自己的规范,同时对Java SPI存在的问题进行了改进,优点如下:

  • 扩展类按需加载,节约资源。
  • SPI文件采用Key=Value形式,可以根据扩展名灵活获取实现类。
  • 扩展类对象做了缓存,避免重复创建。
  • 扩展类加载失败有详细日志,方便排查。
  • 支持AOP和IOC。

Dubbo SPI使用规范:

  • 编写接口,接口必须加@SPI注解,代表它是一个可扩展的接口。
    编写实现类。
  • 在ClassPath下的META-INF/dubbo目录创建以接口全限定名命名的文件,文件内容为Key=Value格式,Key是扩展点的名称,Value是扩展点实现类的全限定名。
  • 通过ExtensionLoader类获取扩展点实现。

测试

spi简单测试,aop测试(wrapper类)

首先创建个spi测试接口Car,和对应的两个实现RedCar和BlackCar;

import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;

@SPI
public interface Car {

    @Adaptive(value = "carType")
    void getColor(URL url);
    
}

实现:

import org.apache.dubbo.common.URL;
import org.example.api.Car;

public class RedCar implements Car {
    @Override
    public void getColor(URL url) {
        System.out.println("red");
    }
}

// BlackCar:
import org.example.api.Car;

import org.apache.dubbo.common.URL;

public class BlackCar implements Car {
    @Override
    public void getColor(URL url) {
        System.out.println("black");
    }
}


aop测试类CarCheckWrapper类:

import org.apache.dubbo.common.URL;
import org.example.api.Car;

public class CarCheckWrapper implements Car {
    private Car car;

    public CarCheckWrapper(Car car) {
        this.car = car;
    }

    @Override
    public void getColor(URL url) {
        System.out.println("before...");
        car.getColor(url);
        System.out.println("after....");
    }
}

在Resource下建立META-INF.services文件夹,创建org.example.api.Car的文件,文件配置上key,value的实现,和wrapper实现;

black=org.example.BlackCar
red=org.example.RedCar
org.example.CarCheckWrapper

测试方法:

public class DubboSPITest {

    @Test
    public void car() throws Exception {
        ExtensionLoader<Car> extensionLoader =
            ExtensionLoader.getExtensionLoader(Car.class);
        Car red = extensionLoader.getExtension("red");
        red.getColor(null);
    }

}

执行结果:
在这里插入图片描述

spi的aop测试

新建接口Driver类,在Driver类里注入Car类,然后执行对应的Car方法.对应传入的Car通过入参URL里的Map来获取.Map的值配置在Car接口getColor方法上的注解@Adaptive中:

import org.apache.dubbo.common.extension.SPI;

import org.apache.dubbo.common.URL;

@SPI
public interface Driver {

    public void driverCar(URL url);

}

实现类:


import org.example.api.Car;
import org.example.api.Driver;
import org.apache.dubbo.common.URL;

public class Trucker implements Driver {

    private Car car;

    public void setCar(Car car) {
        this.car = car;
    }

    @Override
    public void driverCar(URL url) {
        System.out.println("Trucker driverCar...");
        car.getColor(url);
    }

}

在这里插入图片描述
然后同样需要将Driver接口类配置在META-INF.services文件夹中,然后创建文件:org.example.api.Driver文件:

trucker=org.example.Trucker

测试类,通过构建URL的map里key为carType来获取哪个类型的car.


import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.example.api.Car;
import org.example.api.Driver;
import org.junit.jupiter.api.Test;

import java.util.HashMap;

public class DubboSPITest {

    @Test
    public void car() throws Exception {
        ExtensionLoader<Car> extensionLoader =
            ExtensionLoader.getExtensionLoader(Car.class);
        Car red = extensionLoader.getExtension("red");
        red.getColor(null);
    }

    @Test
    public void driverCar() throws Exception {
        ExtensionLoader<Driver> extensionLoader =
                ExtensionLoader.getExtensionLoader(Driver.class);
        Driver trucker = extensionLoader.getExtension("trucker");

        HashMap<String, String> map = new HashMap<>();
        map.put("carType", "black");
        URL url = new URL("","",0,map);
        trucker.driverCar(url);

    }

}

执行结果:
在这里插入图片描述

源码分析

ExtensionLoader入口

我们首先通过 ExtensionLoader 的 getExtensionLoader 方法获取一个 ExtensionLoader 实例,然后再通过 ExtensionLoader 的 getExtension 方法获取拓展类对象。
获取ExtensionLoader实例比较简单,没有就new一个,主要是添加了缓存.

   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 an interface!");
        }
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type (" + type +
                    ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
        }

        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;
    }
 public T getExtension(String name) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        if ("true".equals(name)) {
            // 获取默认的拓展实现类
            return getDefaultExtension();
        }
        // 持有者,持有目标对象
        Holder<Object> holder = getOrCreateHolder(name);
        Object instance = holder.get();
        // 双重校验锁
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    // 创建拓展实例
                    instance = createExtension(name);
                    // 设置到holder中
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

createExtension创建拓展对象的过程

createExtension 方法的逻辑稍复杂一下,包含了如下的步骤:

  • 通过 getExtensionClasses 获取所有的拓展类
  • 通过反射创建拓展对象
  • 向拓展对象中注入依赖
  • 将拓展对象包裹在相应的 Wrapper 对象中

injectExtension方法是ioc实现主要方法,
wrapperClasses的遍历主要是aop实现的主要方式.

 private T createExtension(String name) {
        // 从配置文件中加载所有的拓展类,可得到“配置项名称”到“配置类”的映射关系表
        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);
            }
            // 向实例中注入依赖 ioc实现
            injectExtension(instance);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            // 循环创建 Wrapper 实例  aop实现
            if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    // 将当前 instance 作为参数传给 Wrapper 的构造方法,并通过反射创建 Wrapper 实例。
                    // 然后向 Wrapper 实例中注入依赖,最后将 Wrapper 实例再次赋值给 instance 变量
                    // wapper通过构造方法进行包装
                    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);
        }
    }

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;
    }

 

loadExtensionClasses 方法总共做了两件事情,一是对 SPI 注解进行解析,二是调用 loadDirectory 方法加载指定文件夹配置文件。loadExtensionClasses,加载对应配置文件里面的配置,key=value这样
然后通过value获取对应的class类.

  private Map<String, Class<?>> loadExtensionClasses() {
        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;
    }

  private void cacheDefaultExtensionName() {
        // 获取 SPI 注解,这里的 type 变量是在调用 getExtensionLoader 方法时传入的
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            String value = defaultAnnotation.value();
            if ((value = value.trim()).length() > 0) {
                // 对 SPI 注解内容进行切分
                String[] names = NAME_SEPARATOR.split(value);
                if (names.length > 1) {
                    throw new IllegalStateException("More than 1 default extension name on extension " + type.getName()
                            + ": " + Arrays.toString(names));
                }
                // 设置默认名称,参考 getDefaultExtension 方法
                if (names.length == 1) {
                    cachedDefaultName = names[0];
                }
            }
        }
    }

loadDirectory 方法先通过 classLoader 获取所有资源链接,然后再通过 loadResource 方法加载资源。

  private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
        // fileName = 文件夹路径 + 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();
                    // 加载资源
                    loadResource(extensionClasses, classLoader, resourceURL);
                }
            }
        } catch (Throwable t) {
            logger.error("Exception occurred when loading extension class (interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }

private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
        try {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
                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 方法对类进行缓存
                                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);
                        }
                    }
                }
            }
        } catch (Throwable t) {
            logger.error("Exception occurred when loading extension class (interface: " +
                    type + ", class file: " + resourceURL + ") in " + resourceURL, t);
        }
    }

loadClass 方法操作了不同的缓存,比如 cachedAdaptiveClass、cachedWrapperClasses 和 cachedNames 等等。除此之外,该方法没有其他什么逻辑了。

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);
            // 检测 clazz 是否是 Wrapper 类型
        } else if (isWrapperClass(clazz)) {
            // 缓存
            cacheWrapperClass(clazz);
        } else {
            // 检测 clazz 是否有默认的构造方法,如果没有,则抛出异常
            clazz.getConstructor();
            // 可以通过类上面注解Extension来获取name,不过已经过时了
            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);
                }
            }

            // 切分name,比如支持red,red1,red2=org.example.RedCar
            String[] names = NAME_SEPARATOR.split(name);
            if (ArrayUtils.isNotEmpty(names)) {
                // 存储名称到Class的映射关系
                cacheActivateClass(clazz, names[0]);
                for (String n : names) {
                    cacheName(clazz, n);
                    saveInExtensionClass(extensionClasses, clazz, name);
                }
            }
        }
    }

到这类的加载缓存都完成了.

dubbo spi 的ioc

injectExtension方法就是对类的ioc,

private T injectExtension(T instance) {
        try {
            if (objectFactory != null) {
                // 遍历目标类的所有方法
                for (Method method : instance.getClass().getMethods()) {
                    // 检测方法是否以 set 开头,且方法仅有一个参数,且方法访问级别为 public
                    if (isSetter(method)) {
                        /**
                         * Check {@link DisableInject} to see if we need auto injection for this property
                         */
                        if (method.getAnnotation(DisableInject.class) != null) {
                            continue;
                        }

                        // 获取 setter 方法参数类型
                        Class<?> pt = method.getParameterTypes()[0];
                        if (ReflectUtils.isPrimitives(pt)) {
                            continue;
                        }
                        try {
                            // 获取属性名,比如 setName 方法对应属性名 name
                            String property = getSetterProperty(method);
                            // 从 ObjectFactory 中获取依赖对象
                            Object object = objectFactory.getExtension(pt, property);
                            if (object != null) {
                                // 通过反射调用 setter 方法设置依赖
                                method.invoke(instance, object);
                            }
                        } catch (Exception e) {
                            logger.error("Failed to inject via method " + method.getName()
                                    + " of interface " + type.getName() + ": " + e.getMessage(), e);
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return instance;
    }

objectFactory 变量的类型为 AdaptiveExtensionFactory,AdaptiveExtensionFactory 内部维护了一个 ExtensionFactory 列表,用于存储其他类型的 ExtensionFactory。Dubbo 目前提供了两种 ExtensionFactory,分别是 SpiExtensionFactory 和 SpringExtensionFactory。前者用于创建自适应的拓展,后者是用于从 Spring 的 IOC 容器中获取所需的拓展。

SpiExtensionFactory

SpiExtensionFactory就是调用ExtensionLoader的getExtensionLoader方法来获取注入类.

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()) {
                return loader.getAdaptiveExtension();
            }
        }
        return null;
    }

}

SpringExtensionFactory

SpringExtensionFactory就是通过拿到spring容器,塞入对应bean的.
SpringExtensionFactory的容器是在加载ServiceBean跟ReferenceBean的添加的.
在这里插入图片描述
在这里插入图片描述

 @Override
    @SuppressWarnings("unchecked")
    public <T> T getExtension(Class<T> type, String name) {

        //SPI should be get from SpiExtensionFactory
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            return null;
        }

        for (ApplicationContext context : contexts) {
            if (context.containsBean(name)) {
                Object bean = context.getBean(name);
                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;
        }

        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;
    }

dubbo spi的aop

dubbo spi的aop就是通过将执行类包装一层返回wapper类实现的
将执行类通过wapper类的构造方法,塞到wapper类里,如果有多个wapper类就循环塞进去.

   // 循环创建 Wrapper 实例  aop实现
            if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    // 将当前 instance 作为参数传给 Wrapper 的构造方法,并通过反射创建 Wrapper 实例。
                    // 然后向 Wrapper 实例中注入依赖,最后将 Wrapper 实例再次赋值给 instance 变量
                    // wapper通过构造方法进行包装
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }

wapper类demo:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值