Dubbo SPI机制和原理解析

1,412 阅读7分钟

简介

SPI(service provider interface)是一种服务发现机制,通过加载指定路径下配置文件中的实现类,达到运行时用实现动态替换接口的目的。SPI常常用于扩展应用的功能,Dubbo就是通过SPI来加载所有的组件,因此用户可以很方便地扩展Dubbo的功能。Dubbo SPI是在JDK SPI的基础上发展过来的,相比之下,它做了一些改进和优化。

改进(相比JDK SPI)

  • JDK SPI会一次性加载所有扩展点实现,如果有的实现初始化耗时,有的没用上,会比较浪费资源。Dubbo SPI在配置文件中为每个实现类指定key,可通过key去加载对应的实现类。
  • JDK SPI扩展实现加载失败时,会将异常信息吃掉,导致很难定位失败原因。Dubbo则会在异常抛出前写到日志中,同时也会正常抛出异常。
  • Dubbo SPI提供了对IOCAOP的支持,体现在扩展点自动包装和自动装配。

约定

在扩展类的jar包中,将扩展点的配置文件放在META-INF/dubbo/接口全限定名,文件内容为配置名=实现类全限定名,多个实现类用换行符分隔。

示例

以扩展Dubbo的Filter接口为例,现在实现类的jar包META-INF/dubbo/下创建名为com.alibaba.dubbo.rpc.Filter的文件。文件内容为

xxx=com.alibaba.xxx.XxxFilter

实现类

package com.alibaba.xxx;
 
import org.apache.dubbo.rpc.Filter;
 
public class XxxFilter implements Filter{     // ...
}

配置过滤使用指定实现类

dubbo.provider.filter=xxx

扩展点特性

自动包装

加载某个扩展点实现时,如果发现存在参数为扩展点的构造函数,那么这个扩展点实现就是Wrapper类。

Wrapper类内容

package com.alibaba.xxx;
 
import org.apache.dubbo.rpc.Protocol;
 
public class XxxProtocolWrapper implements Protocol {
    Protocol impl;
 
    public XxxProtocolWrapper(Protocol protocol) { impl = protocol; }
 
    // 接口方法做一个操作后,再调用extension的方法
    public void refer() {
        //... 一些操作
        impl.refer();
        // ... 一些操作
    }
 
    // ...
}

Wrapper类持有了真正的扩展点实现类,主要作用是封装扩展点的公用逻辑,类似AOP。

自动注入

如果扩展实现类的成员属性也是一个扩展点,那么ExtensionLoader会加载并注入这个依赖的扩展点。这是Dubbo SPI的IOC特性,依赖扩展点通过setter方法进行注入

自适应

在注入一个扩展点时,当一个扩展点有多个实现类,在程序运行前可能我们并不知道要加载哪个实现类。自适应扩展则支持在运行时根据URL参数动态加载实现。Dubbo提供@Adaptive注解来实现自适应。@Adaptive如果作用于类上,那么标明该类是一个自适应扩展类。作用于方法上,则会运行时生成一个代理类来进行转发,由代理类根据URL中的传参去加载真正的实现

public interface Transporter {
    @Adaptive({"server", "transport"})
    Server bind(URL url, ChannelHandler handler) throws RemotingException;
 
    @Adaptive({"client", "transport"})
    Client connect(URL url, ChannelHandler handler) throws RemotingException;
}

自动激活

对于集合类扩展点,比如Filter、xxxListener,可以同时加载多个实现。此时可以使用自动激活来实现。

源码分析

加载扩展点时序图



在我们加载扩展实现前会先获取一个使用getExtensionLoader方法获取一个ExtensionLoader实例。

getExtensionLoader

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        if (type == null) {
            throw new IllegalArgumentException("Extension type == null");
        } else if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
        } else if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        } else {
            ExtensionLoader<T> loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
            if (loader == null) {
                EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));
                loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
            }

            return loader;
        }
    }

这个方法比较简单,先对参数扩展点类型type进行检查,然后去缓存里面获取ExtensionLoader实例。缓存没有的话,新建一个放入缓存,然后返回。

下面是getExtension方法,该方法是加载实现类的入口。

getExtension

public T getExtension(String name) {
    if (name == null || name.length() == 0)
        throw new IllegalArgumentException("Extension name == null");
    if ("true".equals(name)) {
        // 获取默认的拓展实现类
        return getDefaultExtension();
    }
    // Holder,顾名思义,用于持有目标对象
    Holder<Object> holder = cachedInstances.get(name);
    if (holder == null) {
        cachedInstances.putIfAbsent(name, new Holder<Object>());
        holder = cachedInstances.get(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

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);
        }
        // 向实例中注入依赖
        injectExtension(instance);
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
            // 循环创建 Wrapper 实例
            for (Class<?> wrapperClass : wrapperClasses) {
                // 将当前 instance 作为参数传给 Wrapper 的构造方法,并通过反射创建 Wrapper 实例。
                // 然后向 Wrapper 实例中注入依赖,最后将 Wrapper 实例再次赋值给 instance 变量
                instance = injectExtension(
                    (T) wrapperClass.getConstructor(type).newInstance(instance));
            }
        }
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException("...");
    }
}

上面的方法可以分为以下几个步骤:

  1. 通过getExtensionClasses获取配置文件中的所有实现类
  2. 反射创建实现类
  3. 注入依赖的其他扩展点(IOC)
  4. 创建Wrapper类实例并返回(AOP)

getExtensionClasses方法中主要是去指定路径下读取和解析配置文件,加载实现类。根据扩展类的类型给分别给cachedClasses、cachedAdaptiveClass、cachedWrapperClasses 和 cachedNames赋值。

injectExtension方法是Dubbo SPI的IOC实现,通过setter方法注入依赖。

injectExtension

private T injectExtension(T instance) {
    try {
        if (objectFactory != null) {
            // 遍历目标类的所有方法
            for (Method method : instance.getClass().getMethods()) {
                // 检测方法是否以 set 开头,且方法仅有一个参数,且方法访问级别为 public
                if (method.getName().startsWith("set")
                    && method.getParameterTypes().length == 1
                    && Modifier.isPublic(method.getModifiers())) {
                    // 获取 setter 方法参数类型
                    Class<?> pt = method.getParameterTypes()[0];
                    try {
                        // 获取属性名,比如 setName 方法对应属性名 name
                        String property = method.getName().length() > 3 ? 
                            method.getName().substring(3, 4).toLowerCase() + 
                            	method.getName().substring(4) : "";
                        // 从 ObjectFactory 中获取依赖对象
                        Object object = objectFactory.getExtension(pt, property);
                        if (object != null) {
                            // 通过反射调用 setter 方法设置依赖
                            method.invoke(instance, object);
                        }
                    } catch (Exception e) {
                        logger.error("fail to inject via method...");
                    }
                }
            }
        }
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    }
    return instance;
}

因为依赖的扩展点由ExtensionFactory创建,所以首先判断objectFactory是否为null。然后反射获取实现类的所有方法,遍历过滤setter方法,得到属性名称。通过objectFactory的genExtension方法获取属性名称对应的扩展实现,最后通过反射调用setter方法注入依赖。

自适应扩展机制


getAdaptiveExtension

public T getAdaptiveExtension() {
    // 从缓存中获取自适应拓展
    Object instance = cachedAdaptiveInstance.get();
    if (instance == null) {    // 缓存未命中
        if (createAdaptiveInstanceError == null) {
            synchronized (cachedAdaptiveInstance) {
                instance = cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
                        // 创建自适应拓展
                        instance = createAdaptiveExtension();
                        // 设置自适应拓展到缓存中
                        cachedAdaptiveInstance.set(instance);
                    } catch (Throwable t) {
                        createAdaptiveInstanceError = t;
                        throw new IllegalStateException("fail to create adaptive instance: ...");
                    }
                }
            }
        } else {
            throw new IllegalStateException("fail to create adaptive instance:  ...");
        }
    }

    return (T) instance;
}

获取自适应扩展类的入口,首先检查缓存,缓存未命中调用createAdaptiveExtension方法创建一个自适应扩展实例,最后缓存起来并返回。

createAdaptiveExtension

private T createAdaptiveExtension() {
    try {
        // 获取自适应拓展类,并通过反射实例化
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        throw new IllegalStateException("Can not create adaptive extension ...");
    }
}

该方法首先调用getAdaptiveExtensionClass方法得到自适应扩展类的Class对象,然后通过反射实例化,最后调用injectExtension注入依赖。如果@Adaptive注解是作用在方法上,那么运行时自动生成的代理类的属性里面是不会有其他扩展点,也就不用注入依赖。这是主要是针对@Adaptive注解修饰在类上的情况。

getAdaptiveExtensionClass

private Class<?> getAdaptiveExtensionClass() {
    // 通过 SPI 获取所有的拓展类
    getExtensionClasses();
    // 检查缓存,若缓存不为空,则直接返回缓存
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    // 创建自适应拓展类
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

先通过getExtensionClasses获取配置文件配置的所有实现类,然后检查缓存,缓存不为空则返回。缓存为空,调用createAdaptiveExtensionClass创建自适应扩展类。

createAdaptiveExtensionClass

private Class<?> createAdaptiveExtensionClass() {
    // 构建自适应拓展代码
    String code = createAdaptiveExtensionClassCode();
    ClassLoader classLoader = findClassLoader();
    // 获取编译器实现类
    com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    // 编译代码,生成 Class
    return compiler.compile(code, classLoader);
}

这个方法首先会生成代理类的源代码,然后使用Complier编译器编译得到代理类的Class。至于

createAdaptiveExtensionClassCode如何生成源码,代码较长不做分析,可以参考官方文档dubbo.apache.org/zh-cn/docs/…。 

总结

Dubbo SPI是Dubbo的一大特性,它使得Dubbo具有很好的灵活性、便于扩展,对开发者非常友好。其实现本质上也就是规范扩展的步骤和方法,加载扩展点。同时还引入了自动包装、自动注入、自适应、自动激活等特性功能,以满足更多的业务场景。源码部分除了生成自适应代理类比较长,其它都不难理解。