dubbo源码解析之SPI扩展机制(一)

2019-03-19  本文已影响0人  binecy

源码分析基于dubbo 2.7.1-release

看dubbo源码不得不了解dubbo spi机制,因为你常常看到如下代码,而debug又不知所踪

Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

dubbo spi机制增强了Java 原生的 SPI 机制。简单来说,ExtensionLoader支持某个扩展接口存在多个扩展类(如Protocol,ProxyFactory),并通过键值对的方式进行配置,dubbo优先使用用户配置扩展类,如果用户没有配置,就使用默认扩展类。

先看看ExtensionLoader.getExtensionLoader

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    ...
    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;
}

比较简单,就是从缓存中获取,获取失败则创建一个ExtensionLoader。

再看看getAdaptiveExtension,同样查询缓存,缓存查询失败则调用createAdaptiveExtension生成一个扩展类Extension

private T createAdaptiveExtension() {
    try {
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        ...
    }
}

injectExtension负责调用扩展类Extension的set方法,注入对应属性。
getAdaptiveExtensionClass获取扩展类的Class,再通过newInstance实例化一个对象。

查看getAdaptiveExtensionClass方法

private Class<?> getAdaptiveExtensionClass() {
    getExtensionClasses();
    if (cachedAdaptiveClass != null) {  // 检查缓存
        return cachedAdaptiveClass;
    }
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

getExtensionClasses会检查cachedClasses,如果cachedClasses为空,它会调用loadExtensionClasses,加载用户配置的扩展类,并存放到cachedClasses中。

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

变量DUBBO_DIRECTORY的值为META-INF/dubbo/,我们自定义的扩展配置就是放到该路径下。
type.getName().replace("org.apache", "com.alibaba")是因为dubbo成为apace项目后,修改pageage。
loadDirectory会读取指定目录下的配置文件,加载class后存放到cachedClasses。
如dubbo-rpc\dubbo-rpc-dubbo\src\main\resources\META-INF\dubbo\internal\org.apache.dubbo.rpc.Protocol
内容为:

dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol

cachedClasses中的map中会添加dubbo:Class<DubboProtocol>的键值对。

createAdaptiveExtensionClass会生成一个通用的ExtensionClass

private Class<?> createAdaptiveExtensionClass() {
    // 生成代码字符串
    String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
    ClassLoader classLoader = findClassLoader();
    // 编译成class
    com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    // 编译为Class
    return compiler.compile(code, classLoader);
}

AdaptiveClassCodeGenerator会拼凑生成代码字符串,并通过Compiler编译成class。

AdaptiveClassCodeGenerator这里不深入,主要是通过接口的@SPI,@Adaptive注解获取对应的默认配置。
如Protocol接口

@SPI("dubbo")
public interface Protocol {
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
    ...
}

生成的代码如下

package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
    public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
        String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
        if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
        com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.export(arg0);
    }
    ...
}

关键代码是String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() ),这里通过invoker.url中的protocol,再通过getExtension方法找到对应的实现类。如dubbo://...registry://...分别对应DubboProtocolRegistryProtocol
默认使用dubbo协议,这个默认值正是从Protocol接口的@SPI("dubbo")注解中获取

这是一个代理类,当我们调用该类的export方法,会通过ExtensionLoader.getExtension获取真正逻辑类,并调用逻辑类export方法。

最后看看getExtension

public T getExtension(String name) {
    ...
    // 获取Holder
    Holder<Object> holder = cachedInstances.get(name);
    if (holder == null) {
        cachedInstances.putIfAbsent(name, new Holder<Object>());
        holder = cachedInstances.get(name);
    }
    // 获取instance 
    Object instance = holder.get();
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                instance = createExtension(name);
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}

最后看看createExtension

private T createExtension(String name) {
    // 获取ExtensionClass
    Class<?> clazz = getExtensionClasses().get(name);
    if (clazz == null) {
        throw findException(name);
    }
    try {
        // 初始化ExtensionClass
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
            EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        // 注入属性
        injectExtension(instance);
        // 获取wrapperClasse
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (wrapperClasses != null && wrapperClasses.size() > 0) {
            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);
    }
}

getExtensionClasses会查询cachedClasses,前面已经将配置中的Extension信息添加到这里了。

这里要注意,如果存在wrapperClasses,会创建对应的wrapperClasses来装饰目标类。wrapperClasses就是典型的装饰模式。
如Protocol就有ProtocolListenerWrapper,ProtocolFilterWrapper,QosProtocolWrapper等装饰类。

ExtensionLoader加载扩展类时,如果一个扩展类有一个构造方法,参数是它的扩展接口,就会加入到cachedWrapperClasses中。

上一篇 下一篇

猜你喜欢

热点阅读