深入浅出Dubbo——Dubbo SPI(一)



说实话,在手机这种窄屏设备上,不是很适合看源码分析,还是建议你打开电脑,clone一下dubbo代码,然后继续...我尽量把读者当小白,事无巨细的介绍,就怕漏了你想要的。


我想要什么?我想要你关注一下,转发一下呗,自行车就不要了。


深入浅出Dubbo——Dubbo SPI(一)_第1张图片

在上一篇介绍了JDK SPI的使用以及大致实现,其中提到了JDK SPI的最大缺陷就是:不管你需要哪个实现类,java.util.ServiceLoader 会为所有实现类都创建对象


Dubbo为了解决这个问题自己搞了一套SPI,对应的配置文件也改成了kV的格式,例如:

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

其中key就是一个简单的标记,我们指定了”dubbo“,Dubbo SPI根据配置文件就知道我们要的是 org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol 实现类


Dubbo SPI 核心实现是ExtensionLoader【在dubbo-common模块中的extension包中】,对应java.util.ServiceLoader,其使用方式如下:


// 查找Protocol接口的实现——DubboProtocol,并创建其对象	
Protocol protocol = 	
      ExtensionLoader	
      .getExtensionLoader(Protocol.class)	
      .getExtension("dubbo");

知道是干啥的了,知道咋用了,就不叨叨那些没用的了,看代码吧!

1、getExtensionLoader()方法


ExtensionLoader.getExtensionLoader()方法其实就是查缓存,没啥特别的╮(╯_╰)╭,缓存没查到就创建新对象,show you code:


// 全局的缓存,晓得伐	
private static final ConcurrentMap, ExtensionLoader> EXTENSION_LOADERS 	
     = new ConcurrentHashMap<>();	
     	
private final Class type; // 用来记录查询	
	
public static  ExtensionLoader getExtensionLoader(Class type) {	
    // 检测一堆东西...例如,检测type是否为一个接口,检测是否被@SPI注解标识(略)	
    // @SPI注解的功能后面说	
    // 每个接口对应一个ExtensionLoader实例,没有就创建,EXTENSION_LOADERS是个缓存	
    ExtensionLoader loader = (ExtensionLoader) EXTENSION_LOADERS.get(type);	
    if (loader == null) {	
        EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));	
        loader = (ExtensionLoader) EXTENSION_LOADERS.get(type);	
    }	
    return loader;	
}



2、getExtension()方法


跟着我走,别懵逼。getExtension()方法干了两件事:一个是根据传入name查找相应的实现类,二是创建(实现类)对象返回:

// 又是一个缓存,存name->对象Holder之间的映射关系	
private final ConcurrentMap> cachedInstances = new ConcurrentHashMap<>();	
// 先来说一下Holder是个啥?就是一个泛型容器,一个volatile变量+getter/setter方法,	
// 后面会经常见它,它会与后面的synchronized解决并发问题	
public T getExtension(String name) {	
    Holder holder = getOrCreateHolder(name);	
    Object instance = holder.get();	
    if (instance == null) { // double-check防止并发问题	
        synchronized (holder) {	
            instance = holder.get();	
            if (instance == null) {	
                // createExtension()方法是真正完成查找和实例化的地方	
                instance = createExtension(name);	
                holder.set(instance);	
            }	
        }	
    }	
    return (T) instance;	
}


3、createExtension()方法

先明确createExtension()方法从哪里加载配置文件呢?createExtension()方法会调用loadExtensionClasses()方法加载配置文件:


private static final String SERVICES_DIRECTORY = "META-INF/services/";	
	
private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";	
	
private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";	
	
private Map> loadExtensionClasses() {	
    cacheDefaultExtensionName(); // 获取@SPI注解中指定的默认实现	
    // 从"/META-INF/dubbo/""/META-INF/dubbo/internal/"、"/META-INF/services/"三个指定目录	
    // 查找配置文件	
    Map> 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;	
}

loadDirectory()方法会解析KV格式的配置文件,对于每一行都会调用loadClass()方法进行处理


private void loadClass(Map> extensionClasses, java.net.URL resourceURL, Class clazz, String name) throws NoSuchMethodException {	
    // 检查加载到实现类是否实现了type接口(略)	
    if (clazz.isAnnotationPresent(Adaptive.class)) {	
        cacheAdaptiveClass(clazz); // 记录@Adaptive注解标记的实现类	
    } else if (isWrapperClass(clazz)) {	
        cacheWrapperClass(clazz); // 记录wrapper实现类	
    } else {	
        clazz.getConstructor(); // 尝试获取一下无参构造方法	
        // 配置文件里面没指定key,就从实现类的@Extension注解上找(略)	
        String[] names = NAME_SEPARATOR.split(name);	
        if (ArrayUtils.isNotEmpty(names)) {	
            cacheActivateClass(clazz, names[0]);	
            for (String n : names) {	
                cacheName(clazz, n); // 缓存实现类到key的映射	
                // 将key到实现类Class的映射关系记录到extensionClasses集合	
                saveInExtensionClass(extensionClasses, clazz, name);	
            }	
        }	
    }

@Adaptive注解和Wrapper的事情我们下一篇再说,这里只要知道会把key到Class的映射缓存起来就行。


加载完成Class就可以实例化了,createExtension()方法具体实现如下:



// 没错,EXTENSION_INSTANCES又是一个全局的缓存,key是实现类的Class,value是相应的对象	
private static final ConcurrentMap, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>();	
	
private T createExtension(String name) {	
    // 到指定位置查找配置文件,其中会解析配置文件,并加载	
    Class clazz = getExtensionClasses().get(name);	
    // 查询对象缓存	
    T instance = (T) EXTENSION_INSTANCES.get(clazz);	
    if (instance == null) {  // 缓存没有就创建	
        EXTENSION_INSTANCES.putIfAbsent(clazz, 	
              clazz.newInstance());	
        instance = (T) EXTENSION_INSTANCES.get(clazz);	
    }	
    injectExtension(instance); // 注入到某个地方,暂且不说	
    // Wrapper类的处理(略)	
    return instance;	
    // 这里为了方便阅读,省略了try/catch	
}


好了,Dubbo SPI的基本实现就介绍的差不多了。


下一篇继续围绕Dubbo SPI,介绍@Adaptive注解、Wrapper、@Activate是怎么玩的。


深入浅出Dubbo——Dubbo SPI(一)_第2张图片



你可能感兴趣的:(Dubbo,Java)