Dubbo SPI

META-INF/dubbo下的文件可以使用#注释

ExtensionLoader

  • 基本字段说明

以下字段可以分为class字段(如cacheNames)与实例字段(如cacheInstances)

public class ExtensionLoader<T> {
    //对不同的接口记录对应的ExtensionLoader
    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>();
    //对不同的class记录产生的实例,共享所有接口对应的实现实例
    private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>();
    //loader对应的接口
    private final Class<?> type;
    //对接口ExtensionFactory是null,其余接口是AdaptiveExtensionFactory实例
    private final ExtensionFactory objectFactory;
    //记录当前接口对应的实现class及className
    private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
    //Holder内部是volatile,记录className及对应的class
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
    private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>();
    //记录当前接口对应的className及实现实例Holder
    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
    //适配实例,loader不指定实现名称时使用适配实例
    private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
    //适配class
    private volatile Class<?> cachedAdaptiveClass = null;
    //接口中@SPI声明的名称
    private String cachedDefaultName;
    private volatile Throwable createAdaptiveInstanceError;
    //实现外层包裹的多层装饰类
    private Set<Class<?>> cachedWrapperClasses;
    private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<>();
}

核心逻辑研究

  • 使用原则
//获取META-INF配置文件中包含哪些实现
extensionLoader<T>.getExtensionClass()
//当上下文逻辑中指定实现名称时使用
extensionLoader<T>.getExtension(name);
//当上下文逻辑中没有指定名称时使用
extensionLoader<T>.getAdaptiveExtension()
  • 测试入口
//测试代码
public void testDubboSpi() {
  //初始化ExtensionLoader<ExtensionFactory>实例(class字段及实例字段)
  //初始化ExtensionLoader<Robot>实例,仅设置type和objectFactory字段,不会初始化class相关字段
  ExtensionLoader<Robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class);
  // 内部调用extensionLoader.getExtensionClass初始化class相关字段,获取包裹了wrapper的实例
  Robot bumblebee = extensionLoader.getExtension("bumblebee");
  bumblebee.sayHello();
  // 测试自动注入Battery字段
  Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
  optimusPrime.sayHello();
  // 获取自动注入
  Robot compoundRobot = extensionLoader.getExtension("compound");
  compoundRobot.sayHello();
}
  • ExtensionLoader.getExtensionLoader(Robot.class)
//函数调用链
ExtensionLoader.getExtensionLoader(Robot.class)
ExtensionLoader.getExtensionLoader(ExtensionFactory.class))
//未指定实现,获取adaptive实现
ExtensionLoader<ExtensionFactory>.getAdaptiveExtension()
//获取AdaptiveExtensionClass并初始化adaptive实例
getAdaptiveExtensionClass().newInstance()
//获取META-INF中对应的名称与实现的对应关系
getExtensionClasses()
//扫描META-INF/dubbo等默认配置路径
loadExtensionClasses()
//根据配置文件中名称与实现初始化loader中class字段
loadClass()
//newInstance()时调用默认构造函数,初始化loader中实例字段并设置SpiFactory和SpringFactory
AdaptiveExtensionFactory()
//将AdaptiveExtensionFactory设置到ExtensionLoader<Robot>中

private ExtensionLoader(Class<?> type) {
    this.type = type;
    //第一次调用ExtensionLoader.getExtensionLoader(type)时初始化ExtensionLoader<ExtensionFactory>实例
    //初始化实例时仅设置type和objectFactory字段
    //type!=ExtensionFactory时objectFactory为AdaptiveExtensionFactory实例
    objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

private T createAdaptiveExtension() {
    //newInstance()时会调用默认构造函数,如AdaptiveExtensionFactory()
    //这里调用injectExtension方法的目的是为手工编码的自适应拓展注入依赖
    return injectExtension((T) getAdaptiveExtensionClass().newInstance());
}

private Class<?> getAdaptiveExtensionClass() {
    getExtensionClasses();
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

private Map<String, Class<?>> loadExtensionClasses() {
    //根据@SPI("")设置defaultName
    cacheDefaultExtensionName();
    Map<String, Class<?>> extensionClasses = new HashMap<>();
    //在以下目录寻找SPI配置并初始化本ExtensionLoader中的相关字段
    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;
}

//处理spi配置中每一行记录
//设置AdaptiveClass,WrapperClass,class->className,className->class字段
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
    if (clazz.isAnnotationPresent(Adaptive.class)) {
        //设置cachedAdaptiveClass字段,多于一个AdaptiveClass抛出异常
        cacheAdaptiveClass(clazz);
    //通过clazz.getConstructor(type)是否报异常来判断
    } else if (isWrapperClass(clazz)) {
        //设置cachedWrapperClasses字段
        cacheWrapperClass(clazz);
    } else {
        // 检测 clazz 是否有默认的构造方法,如果没有,则抛出异常
        clazz.getConstructor();
        String[] names = NAME_SEPARATOR.split(name);
        //判断并设置cachedActivates字段
        cacheActivateClass(clazz, names[0]);
        for (String n : names) {
            //设置cachedNames
            cacheName(clazz, n);
            //返回后设置cachedClasses
            saveInExtensionClass(extensionClasses, clazz, name);
        }
    }
}

public AdaptiveExtensionFactory() {
    ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
    List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
    //初始化SpiExtensionFactory和SpringExtensionFactory实例,设置cachedInstances字段和EXTENSION_INSTANCES字段
    for (String name : loader.getSupportedExtensions()) {
        list.add(loader.getExtension(name));
    }
    factories = Collections.unmodifiableList(list);
}
  • extensionLoader<Robot>.getExtension("bumblebee")
//META-INF中配置
bumblebee = com.my.study.business.impl.dubbo.pojo.BumblebeeRobot
wrapper = com.my.study.business.impl.dubbo.pojo.RobotWrapper

//初始化实例
//Ioc注入,Wrapper处理
private T createExtension(String name) {
    //初始化当前loader涉及的实现class(不初始化实例)
    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);
    }
    // Ioc注入其他实现字段
    injectExtension(instance);
    Set<Class<?>> wrapperClasses = cachedWrapperClasses;
    if (CollectionUtils.isNotEmpty(wrapperClasses)) {
        for (Class<?> wrapperClass : wrapperClasses) {
            //支持多个wrapper,多重代理,实现的不错
            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
        }
    }
    return instance;
}
  • extensionLoader.getExtension("optimusPrime")
//函数调用链
//extensionLoader<Robot>.getExtension时扫描
getExtension(robot).injectExtension(instance)
adaptiveExtensionFactory.getExtension()
//若type声明了@Spi,则走spiExtensionFactory,否则走springExtensionFactory,如何初始化SpringExtensionFactory呢?
spiExtensionFactory.getExtension()
//未指定battery实现,获取AdaptiveExtension
extensionLoader<battery>.getAdaptiveExtension()
//获取AdaptiveExtensionClass:首先判断META-INF配置文件中是否指定AdaptiveExtensionClass,如果未指定则临时创建代理类
extensionLoader<battery>.getAdaptiveExtensionClass()
//创建代理类
Class c=createAdaptiveExtensionClass()
//创建代理类实例并在上文injectExtension(instance)中通过setter方法注入

private T injectExtension(T instance) {
if (objectFactory != null) {
 for (Method method : instance.getClass().getMethods()) {
   //找到setter方法
   if (isSetter(method)) {
     String property = getSetterProperty(method);
     //adaptiveExtensionFactory调用,property其实无用
     Object object = objectFactory.getExtension(pt, property);
     if (object != null) {
       //set回去
       method.invoke(instance, object);
                    
private Class<?> getAdaptiveExtensionClass() {
    getExtensionClasses();
    if (cachedAdaptiveClass != null) {
        //如果META-INF中配置了AdaptiveExtensionClass则直接使用(如AdaptiveExtensionFactory.class)
        return cachedAdaptiveClass;
    }
    //如未配置,则需要临时创建
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

private Class<?> createAdaptiveExtensionClass() {
    //创建代理类java code,code中每个函数根据入参Url及type函数@Adaptive注解指定的名称选择并调用实现中对应的函数。
    //接口中某method未声明@Adaptive时,代理类中该method body为throw UnsupportOperationException()
    //为什么该代理类要临时创建?因为需要继承对应的接口并实现相应的方法,无法使用通用的逻辑,创建并完成Ioc注入后后续调用可以直接使用。
    String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
    ClassLoader classLoader = findClassLoader();
    //选择创建代理类的库,使用META-INF文件中指定的AdaptiveCompiler
    org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    return compiler.compile(code, classLoader);
}

//AdaptiveCompiler
public Class<?> compile(String code, ClassLoader classLoader) {
    Compiler compiler;
    ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
    String name = DEFAULT_COMPILER; 
    // 使用设置的compile
    if (name != null && name.length() > 0) {
        compiler = loader.getExtension(name);
    } else {
        //使用默认的compiler,默认是JavassistByte
        compiler = loader.getDefaultExtension();
    }
    return compiler.compile(code, classLoader);
}

//JavassitByte生成的代码
package com.my.study.business.impl.dubbo.pojo.battery;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Battery$Adaptive implements com.my.study.business.impl.dubbo.pojo.battery.Battery {

public void uninstall(org.apache.dubbo.common.URL arg0)  {
//未声明@Activate的method直接写入异常抛出
throw new UnsupportedOperationException("The method public abstract void com.my.study.business.impl.dubbo.pojo.battery.Battery.uninstall(org.apache.dubbo.common.URL) of interface com.my.study.business.impl.dubbo.pojo.battery.Battery is not adaptive method!");
}

public void install(org.apache.dubbo.common.URL arg0)  {
   if (arg0 == null) throw new IllegalArgumentException("url == null");
   org.apache.dubbo.common.URL url = arg0;
   //从url中获取实现的名称
   String extName = url.getParameter("id", url.getParameter("type"));
   if(extName == null) throw new IllegalStateException("Failed to get extension (com.my.study.business.impl.dubbo.pojo.battery.Battery) name from url (" + url.toString() + ") use keys([id, type])");
   com.my.study.business.impl.dubbo.pojo.battery.Battery extension = (com.my.study.business.impl.dubbo.pojo.battery.Battery)ExtensionLoader.getExtensionLoader(com.my.study.business.impl.dubbo.pojo.battery.Battery.class).getExtension(extName);
   extension.install(arg0);
}
}
  • batteryExtensionLoader.getActivateExtension(URL.valueOf("?type=water"), new String[] {"nuclear"}, null)

与Ioc无关,直接调用getActivateExtension()才会获取满足@Activate条件的Extension

public List<T> getActivateExtension(URL url, String[] values, String group) {
//获取META-INF配置中未被new String[] {"nuclear"}指定且入参group、url与@Activate.group和value值不违背的extension
if (isMatchGroup(group, activateGroup)) {
    T ext = getExtension(name);
    if (!names.contains(name)
            && !names.contains(REMOVE_VALUE_PREFIX + name)
            && isActive(activateValue, url)) {
        exts.add(ext);
    }
}
//获取META-INF配置中由new String[] {"nuclear"}指定的extension
for (int i = 0; i < names.size(); i++) {
    String name = names.get(i);
    if (!name.startsWith(REMOVE_VALUE_PREFIX)
            && !names.contains(REMOVE_VALUE_PREFIX + name)) {
        T ext = getExtension(name);
        usrs.add(ext);

    }
}
//组合以上结果
exts.addAll(usrs);

DubboSpiIoc vs SpringIoc

DubboSpi与SpringIoc二者都是从配置依赖注入策略。

  • SpringIoc

Spring可以通过@Component注入策略,可以使用@Qualifier引用注入,可以选择lazyInit延迟初始化,可以使用applicationContext.getBean(name,class)根据函数参数使用对应的实现

  • DubboSpiIoc

在meta中配置策略,通过ExtensionLoader手动获取指定的策略,支持Ioc扫描注入临时产生的代理类以实现参数动态策略选择。

DubboSpi包含的功能SpringIoc都有,为啥还要使用DubboSpi?是为了保持轻量和解耦Spring吗?

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • dubbo自定义SPI 为什么dubbo要自己设计一套SPI? JDK标准的SPI会一次性实例化扩展点所有实现,如...
    半个橙子阅读 651评论 0 9
  • Java提供了SPI机制(ServiceLoader)来进行服务发现,而Dubbo中的扩展点同样使用了SPI机制进...
    言西枣阅读 392评论 0 0
  • SPI简介 站在一个框架作者的角度来说,定义一个接口,自己默认给出几个接口的实现类,同时 允许框架的使用者也能够自...
    fffhJk阅读 487评论 0 1
  • title: Dubbo SPI机制分析tags: Dubbo,SPI,源码grammar_cjkRuby: tr...
    liu_c02c阅读 2,042评论 0 0
  • 总结:Protocolrefprotocol = ExtensionLoader.getExtensionLoad...
    Ngcc阅读 703评论 0 2