Dubbo中SPI擴(kuò)展機(jī)制詳解

前面我們了解過(guò)了Java的SPI擴(kuò)展機(jī)制莲绰,對(duì)于Java擴(kuò)展機(jī)制的原理以及優(yōu)缺點(diǎn)也有了大概的了解枯夜,這里繼續(xù)深入一下Dubbo的擴(kuò)展點(diǎn)加載機(jī)制。

Dubbo擴(kuò)展點(diǎn)加載的功能

Dubbo的擴(kuò)展點(diǎn)加載機(jī)制類(lèi)似于Java的SPI矾瑰,我們知道Java的SPI在使用的時(shí)候倦蚪,只能通過(guò)遍歷來(lái)進(jìn)行實(shí)現(xiàn)的查找和實(shí)例化,有可能會(huì)一次性把所有的實(shí)現(xiàn)都實(shí)例化痢法,這樣會(huì)造成有些不使用的擴(kuò)展實(shí)現(xiàn)也會(huì)被實(shí)例化狱窘,這就會(huì)造成一定的資源浪費(fèi)。有關(guān)Dubbo的改進(jìn)财搁,參照文檔上的說(shuō)明:

  • JDK標(biāo)準(zhǔn)的SPI會(huì)一次性實(shí)例化擴(kuò)展點(diǎn)所有實(shí)現(xiàn)蘸炸,如果有擴(kuò)展實(shí)現(xiàn)初始化很耗時(shí),但如果沒(méi)用上也加載妇拯,會(huì)很浪費(fèi)資源幻馁。
  • 如果擴(kuò)展點(diǎn)加載失敗,連擴(kuò)展點(diǎn)的名稱(chēng)都拿不到了越锈。比如:JDK標(biāo)準(zhǔn)的ScriptEngine仗嗦,通過(guò)getName();獲取腳本類(lèi)型的名稱(chēng),但如果RubyScriptEngine因?yàn)樗蕾嚨膉ruby.jar不存在甘凭,導(dǎo)致RubyScriptEngine類(lèi)加載失敗稀拐,這個(gè)失敗原因被吃掉了,和ruby對(duì)應(yīng)不起來(lái)丹弱,當(dāng)用戶執(zhí)行ruby腳本時(shí)德撬,會(huì)報(bào)不支持ruby铲咨,而不是真正失敗的原因。
  • 增加了對(duì)擴(kuò)展點(diǎn)IoC和AOP的支持蜓洪,一個(gè)擴(kuò)展點(diǎn)可以直接setter注入其它擴(kuò)展點(diǎn)纤勒。

關(guān)于第一點(diǎn),通過(guò)和Java的SPI對(duì)比隆檀,就能明白摇天;第二點(diǎn)還未做測(cè)試,不太清楚其中的緣由恐仑;第三點(diǎn)對(duì)于IOC和AOP的支持下面簡(jiǎn)單介紹下泉坐。

擴(kuò)展點(diǎn)自動(dòng)裝配功能(IOC)

就是當(dāng)加載一個(gè)擴(kuò)展點(diǎn)時(shí),會(huì)自動(dòng)的注入這個(gè)擴(kuò)展點(diǎn)所依賴的其他擴(kuò)展點(diǎn)裳仆,如果描述不清楚的話腕让,可以看下下面的例子:

接口A,實(shí)現(xiàn)類(lèi)A1歧斟,A2
接口B纯丸,實(shí)現(xiàn)類(lèi)B1,B2

其中實(shí)現(xiàn)類(lèi)A1含有setB()方法构捡,當(dāng)通過(guò)擴(kuò)展機(jī)制加載A的實(shí)現(xiàn)的時(shí)候液南,會(huì)自動(dòng)的注入一個(gè)B的實(shí)現(xiàn)類(lèi),但是勾徽,此時(shí)不是注入B1,也不是注入B2统扳,而是注入一個(gè)自適應(yīng)的B的實(shí)現(xiàn)類(lèi):B$Adpative喘帚,該實(shí)現(xiàn)類(lèi)是動(dòng)態(tài)生成的,能夠根據(jù)參數(shù)的不同咒钟,自動(dòng)選擇B1或者B2來(lái)進(jìn)行調(diào)用吹由。

擴(kuò)展點(diǎn)自適應(yīng)

上面我們說(shuō),在自動(dòng)裝配的時(shí)候朱嘴,并不是注入一個(gè)真正的實(shí)現(xiàn)倾鲫,而是注入一個(gè)自適應(yīng)的擴(kuò)展點(diǎn)實(shí)現(xiàn),其實(shí)就是動(dòng)態(tài)的生成的代碼萍嬉,也就是手動(dòng)拼裝的代碼乌昔,這段代碼里會(huì)根據(jù)SPI上配置的信息來(lái)加入對(duì)于具體實(shí)現(xiàn)的選擇功能。生成的代碼類(lèi)似于下面的壤追,代碼做了一下精簡(jiǎn)磕道,把包都去掉了:

import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implements Protocol {
  public Invoker refer(Class arg0, URL arg1) throws Class {
    if (arg1 == null) throw new IllegalArgumentException("url == null");

    URL url = arg1;
    String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );

    if(extName == null) throw new IllegalStateException("Fail to get extension(Protocol) name from url(" + url.toString() + ") use keys([protocol])");
    
    Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);
    
    return extension.refer(arg0, arg1);
  }
  
  public Exporter export(Invoker arg0) throws Invoker {
    if (arg0 == null) throw new IllegalArgumentException("Invoker argument == null");
    
    if (arg0.getUrl() == null) throw new IllegalArgumentException("Invoker argument getUrl() == null");URL url = arg0.getUrl();
    //這里會(huì)根據(jù)url中的信息獲取具體的實(shí)現(xiàn)類(lèi)名
    String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
    
    if(extName == null) throw new IllegalStateException("Fail to get extension(Protocol) name from url(" + url.toString() + ") use keys([protocol])");
    //根據(jù)上面的實(shí)現(xiàn)類(lèi)名,會(huì)在運(yùn)行時(shí)行冰,通過(guò)Dubbo的擴(kuò)展機(jī)制加載具體實(shí)現(xiàn)類(lèi)
    Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);
    
    return extension.export(arg0);
  }
  
  public void destroy() {
    throw new UnsupportedOperationException("method public abstract void Protocol.destroy() of interface Protocol is not adaptive method!");
  }
  
  public int getDefaultPort() {
    throw new UnsupportedOperationException("method public abstract int Protocol.getDefaultPort() of interface Protocol is not adaptive method!");
  }
}

使用這種方式的原因也很容易能想到溺蕉,在我們加載擴(kuò)展點(diǎn)實(shí)現(xiàn)的時(shí)候伶丐,并沒(méi)有調(diào)用實(shí)現(xiàn)的具體邏輯,那我們注入一個(gè)擴(kuò)展點(diǎn)疯特,也就不知道這個(gè)擴(kuò)展點(diǎn)的實(shí)現(xiàn)具體是什么哗魂,所以要注入一個(gè)自適應(yīng)的實(shí)現(xiàn)。等到運(yùn)行時(shí)候漓雅,才根據(jù)自適應(yīng)實(shí)現(xiàn)啡彬,來(lái)調(diào)用真正實(shí)現(xiàn)。

擴(kuò)展點(diǎn)自動(dòng)包裝功能(AOP)

先看下下面的示例故硅,假如接口A還有另外一個(gè)實(shí)現(xiàn)者:AWrapper1:

class AWrapper1 implements A{
    private A a;
    AWrapper1(A a){
      this.a = a;
    }
    
}

AWrapper1相當(dāng)于A的包裝類(lèi)庶灿,類(lèi)似于AOP的功能,AWrapper1增加了A的功能吃衅。當(dāng)我們獲取接口A的實(shí)現(xiàn)類(lèi)的時(shí)候往踢,得到的就是包裝過(guò)的類(lèi)。

Dubbo擴(kuò)展點(diǎn)加載的實(shí)現(xiàn)

首先還是定義接口徘层,然后是接口的具體實(shí)現(xiàn)類(lèi)峻呕,配置文件類(lèi)似于Java的SPI配置文件,Dubbo的配置文件放在META-INF/dubbo/目錄下趣效,配置文件名為接口的全限定名瘦癌,配置文件內(nèi)容是配置名=擴(kuò)展實(shí)現(xiàn)類(lèi)的全限定名,加載實(shí)現(xiàn)類(lèi)的功能是通過(guò)ExtensionLoader來(lái)實(shí)現(xiàn)跷敬,類(lèi)似于Java中的ServiceLoader的作用讯私。

另外,擴(kuò)展點(diǎn)使用單一實(shí)例加載西傀,需要確保線程安全性斤寇。

Dubbo擴(kuò)展點(diǎn)加載的一些定義

  • @SPI注解,被此注解標(biāo)記的接口拥褂,就表示是一個(gè)可擴(kuò)展的接口娘锁。

  • @Adaptive注解,有兩種注解方式:一種是注解在類(lèi)上饺鹃,一種是注解在方法上莫秆。

    • 注解在類(lèi)上,而且是注解在實(shí)現(xiàn)類(lèi)上悔详,目前dubbo只有AdaptiveCompiler和AdaptiveExtensionFactory類(lèi)上標(biāo)注了此注解镊屎,這是些特殊的類(lèi),ExtensionLoader需要依賴他們工作伟端,所以得使用此方式杯道。
    • 注解在方法上,注解在接口的方法上,除了上面兩個(gè)類(lèi)之外党巾,所有的都是注解在方法上萎庭。ExtensionLoader根據(jù)接口定義動(dòng)態(tài)的生成適配器代碼,并實(shí)例化這個(gè)生成的動(dòng)態(tài)類(lèi)齿拂。被Adaptive注解的方法會(huì)生成具體的方法實(shí)現(xiàn)驳规。沒(méi)有注解的方法生成的實(shí)現(xiàn)都是拋不支持的操作異常UnsupportedOperationException。被注解的方法在生成的動(dòng)態(tài)類(lèi)中署海,會(huì)根據(jù)url里的參數(shù)信息吗购,來(lái)決定實(shí)際調(diào)用哪個(gè)擴(kuò)展。

    比如說(shuō)這段代碼:

    private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
    

    當(dāng)上面代碼執(zhí)行的時(shí)候砸狞,我們其實(shí)還不知道要真正使用的Protocol是什么捻勉,可能是具體的實(shí)現(xiàn)DubboProtocol,也可能是其他的具體實(shí)現(xiàn)的Protocol刀森,那么這時(shí)候refprotocol到底是什么呢踱启?refprotocol其實(shí)是在調(diào)用getAdaptiveExtension()方法時(shí)候,自動(dòng)生成的一個(gè)類(lèi)研底,代碼如下:

    import com.alibaba.dubbo.common.extension.ExtensionLoader;
    public class Protocol$Adpative implements Protocol {
      public Invoker refer(Class arg0, URL arg1) throws Class {
        if (arg1 == null) throw new IllegalArgumentException("url == null");
    
        URL url = arg1;
        String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
    
        if(extName == null) throw new IllegalStateException("Fail to get extension(Protocol) name from url(" + url.toString() + ") use keys([protocol])");
    
        Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);
    
        return extension.refer(arg0, arg1);
      }
    
      public Exporter export(Invoker arg0) throws Invoker {
        if (arg0 == null) throw new IllegalArgumentException("Invoker argument == null");
    
        if (arg0.getUrl() == null) throw new IllegalArgumentException("Invoker argument getUrl() == null");URL url = arg0.getUrl();
    
        String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
    
        if(extName == null) throw new IllegalStateException("Fail to get extension(Protocol) name from url(" + url.toString() + ") use keys([protocol])");
    
        Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);
    
        return extension.export(arg0);
      }
    
      public void destroy() {
        throw new UnsupportedOperationException("method public abstract void Protocol.destroy() of interface Protocol is not adaptive method!");
      }
    
      public int getDefaultPort() {
        throw new UnsupportedOperationException("method public abstract int Protocol.getDefaultPort() of interface Protocol is not adaptive method!");
      }
    }
    

    可以看到被@Adaptive注解的方法都生成了具體的實(shí)現(xiàn)埠偿,并且實(shí)現(xiàn)邏輯都相同。而沒(méi)有被注解的方法直接拋出不支持操作的異常榜晦。

    當(dāng)我們使用refprotocol調(diào)用方法的時(shí)候冠蒋,其實(shí)是調(diào)用生成的類(lèi)Protocol$Adpative中的方法,這里面的方法根據(jù)url中的參數(shù)配置來(lái)找到具體的實(shí)現(xiàn)類(lèi)乾胶,找具體實(shí)現(xiàn)類(lèi)的方式還是通過(guò)dubbo的擴(kuò)展機(jī)制抖剿。比如url中可能會(huì)有protocol=dubbo,此時(shí)就可以根據(jù)這個(gè)dubbo來(lái)確定我們要找的類(lèi)是DubboProtocol胚吁⊙捞桑可以查看下生成的代碼中getExtension(extName)這里是根據(jù)具體的名字去查找實(shí)現(xiàn)類(lèi)。

  • @Activate注解腕扶,此注解需要注解在類(lèi)上或者方法上,并注明被激活的條件吨掌,以及所有的被激活實(shí)現(xiàn)類(lèi)中的排序信息半抱。

  • ExtensionLoader,是dubbo的SPI機(jī)制的查找服務(wù)實(shí)現(xiàn)的工具類(lèi)膜宋,類(lèi)似與Java的ServiceLoader窿侈,可做類(lèi)比。dubbo約定擴(kuò)展點(diǎn)配置文件放在classpath下的/META-INF/dubbo秋茫,/META-INF/dubbo/internal史简,/META-INF/services目錄下,配置文件名為接口的全限定名肛著,配置文件內(nèi)容為配置名=擴(kuò)展實(shí)現(xiàn)類(lèi)的全限定名圆兵。

Dubbo擴(kuò)展點(diǎn)加載的源碼解析

重點(diǎn)解析下ExtensionLoader這個(gè)類(lèi)跺讯。Dubbo的擴(kuò)展點(diǎn)使用單一實(shí)例去加載,緩存在ExtensionLoader中殉农。每一個(gè)ExtensionLoader實(shí)例僅負(fù)責(zé)加載特定SPI擴(kuò)展的實(shí)現(xiàn)刀脏,想要獲得某個(gè)擴(kuò)展的實(shí)現(xiàn),首先要獲得該擴(kuò)展對(duì)應(yīng)的ExtensionLoader實(shí)例超凳。

以Protocol為例進(jìn)行分析擴(kuò)展點(diǎn)的加載:

//這樣使用愈污,先獲取ExtensionLoader實(shí)例,然后加載自適應(yīng)的Protocol擴(kuò)展點(diǎn)
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
//使用
protocol.refer(Class<T> type, URL url))轮傍;

可以看到暂雹,使用擴(kuò)展點(diǎn)加載的步驟大概有三步:

  1. 獲取ExtensionLoader實(shí)例。
  2. 獲取自適應(yīng)實(shí)現(xiàn)创夜。
  3. 使用獲取到的實(shí)現(xiàn)杭跪。

下面我們就以這三步作為分界,來(lái)深入源碼的解析挥下。

獲取ExtensionLoader實(shí)例

第一步揍魂,getExtensionLoader(Protocol.class),根據(jù)要加載的接口Protocol棚瘟,創(chuàng)建出一個(gè)ExtensionLoader實(shí)例现斋,加載完的實(shí)例會(huì)被緩存起來(lái),下次再加載Protocol的ExtensionLoader的時(shí)候偎蘸,會(huì)使用已經(jīng)緩存的這個(gè)庄蹋,不會(huì)再新建一個(gè)實(shí)例:

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    //擴(kuò)展點(diǎn)類(lèi)型不能為空
    if (type == null)
        throw new IllegalArgumentException();
    //擴(kuò)展點(diǎn)類(lèi)型只能是接口類(lèi)型的
    if(!type.isInterface()) {
        throw new IllegalArgumentException();
    }
    //沒(méi)有添加@SPI注解,只有注解了@SPI的才會(huì)解析
    if(!withExtensionAnnotation(type)) {
        throw new IllegalArgumentException();
    }
    //先從緩存中獲取指定類(lèi)型的ExtensionLoader
    //EXTENSION_LOADERS是一個(gè)ConcurrentHashMap迷雪,緩存了所有已經(jīng)加載的ExtensionLoader的實(shí)例
    //比如這里加載Protocol.class限书,就以Protocol.class作為key,以新創(chuàng)建的ExtensionLoader作為value
    //每一個(gè)要加載的擴(kuò)展點(diǎn)只會(huì)對(duì)應(yīng)一個(gè)ExtensionLoader實(shí)例章咧,也就是只會(huì)存在一個(gè)Protocol.class在緩存中
    ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    //緩存中不存在
    if (loader == null) {
        //創(chuàng)建一個(gè)新的ExtensionLoader實(shí)例倦西,放到緩存中去
        //對(duì)于每一個(gè)擴(kuò)展,dubbo中只有一個(gè)對(duì)應(yīng)的ExtensionLoader實(shí)例
        EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
        loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    }
    return loader;
}

上面代碼返回一個(gè)ExtensionLoader實(shí)例赁严,getExtensionLoader(Protocol.class)這一步?jīng)]有進(jìn)行任何的加載工作扰柠,只是獲得了一個(gè)ExtensionLoader的實(shí)例。

ExtensionLoader的構(gòu)造方法

上面獲取的是一個(gè)ExtensionLoader實(shí)例疼约,接著看下構(gòu)造實(shí)例的時(shí)候到底做了什么卤档,我們發(fā)現(xiàn)在ExtensionLoader中只有一個(gè)私有的構(gòu)造方法:

private ExtensionLoader(Class<?> type) {
    //接口類(lèi)型
    this.type = type;
    //對(duì)于擴(kuò)展類(lèi)型是ExtensionFactory的,設(shè)置為null
    //getAdaptiveExtension方法獲取一個(gè)運(yùn)行時(shí)自適應(yīng)的擴(kuò)展類(lèi)型
    //每個(gè)Extension只能有一個(gè)@Adaptive類(lèi)型的實(shí)現(xiàn)程剥,如果么有劝枣,dubbo會(huì)自動(dòng)生成一個(gè)類(lèi)
    //objectFactory是一個(gè)ExtensionFactory類(lèi)型的屬性,主要用于加載需要注入的類(lèi)型的實(shí)現(xiàn)
    //objectFactory主要用在注入那一步,詳細(xì)說(shuō)明見(jiàn)注入時(shí)候的說(shuō)明
    //這里記住非ExtensionFactory類(lèi)型的返回的都是一個(gè)AdaptiveExtensionFactory
    objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

不難理解舔腾,ExtensionFactory是主要是用來(lái)加載被注入的類(lèi)的實(shí)現(xiàn)溪胶,分為SpiExtensionFactory和SpringExtensionFactory兩個(gè),分別用來(lái)加載SPI擴(kuò)展實(shí)現(xiàn)和Spring中bean的實(shí)現(xiàn)琢唾。

獲取自適應(yīng)實(shí)現(xiàn)

上面返回一個(gè)ExtensionLoader的實(shí)例之后载荔,開(kāi)始加載自適應(yīng)實(shí)現(xiàn),加載是在調(diào)用getAdaptiveExtension()方法中進(jìn)行的:

getAdaptiveExtension()-->
                createAdaptiveExtension()-->
                                getAdaptiveExtensionClass()-->
                                                getExtensionClasses()-->
                                                                loadExtensionClasses()

先看下getAdaptiveExtension()方法采桃,用來(lái)獲取一個(gè)擴(kuò)展的自適應(yīng)實(shí)現(xiàn)類(lèi)懒熙,最后返回的自適應(yīng)實(shí)現(xiàn)類(lèi)是一個(gè)類(lèi)名為Protocol$Adaptive的類(lèi),并且這個(gè)類(lèi)實(shí)現(xiàn)了Protocol接口:

public T getAdaptiveExtension() {
    //先從實(shí)例緩存中查找實(shí)例對(duì)象
    //private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
    //在當(dāng)前的ExtensionLoader中保存著一個(gè)Holder實(shí)例普办,用來(lái)緩存自適應(yīng)實(shí)現(xiàn)類(lèi)的實(shí)例
    Object instance = cachedAdaptiveInstance.get();
    if (instance == null) {//緩存中不存在
        if(createAdaptiveInstanceError == null) {
            synchronized (cachedAdaptiveInstance) {
                //獲取鎖之后再檢查一次緩存中是不是已經(jīng)存在
                instance = cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
                        //緩存中沒(méi)有工扎,就創(chuàng)建新的AdaptiveExtension實(shí)例
                        instance = createAdaptiveExtension();
                        //新實(shí)例加入緩存
                        cachedAdaptiveInstance.set(instance);
                    } catch (Throwable t) {createAdaptiveInstanceError = t; }
                }
            }
        }
    }

    return (T) instance;
}

創(chuàng)建自適應(yīng)擴(kuò)展

緩存中不存在自適應(yīng)擴(kuò)展的實(shí)例,表示還沒(méi)有創(chuàng)建過(guò)自適應(yīng)擴(kuò)展的實(shí)例衔蹲,接下來(lái)就是創(chuàng)建自適應(yīng)擴(kuò)展實(shí)現(xiàn)肢娘,createAdaptiveExtension()方法,用來(lái)創(chuàng)建自適應(yīng)擴(kuò)展類(lèi)的實(shí)例:

private T createAdaptiveExtension() {
    try {
        //先通過(guò)getAdaptiveExtensionClass獲取AdaptiveExtensionClass
        //然后獲取其實(shí)例
        //最后進(jìn)行注入處理
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {}
}

獲取自適應(yīng)擴(kuò)展類(lèi)

接著查看getAdaptiveExtensionClass()方法舆驶,用來(lái)獲取一個(gè)自適應(yīng)擴(kuò)展的Class橱健,這個(gè)Class將會(huì)在下一步被實(shí)例化:

private Class<?> getAdaptiveExtensionClass() {
    //加載當(dāng)前Extension的所有實(shí)現(xiàn)(這里舉例是Protocol,只會(huì)加載Protocol的所有實(shí)現(xiàn)類(lèi))沙廉,如果有@Adaptive類(lèi)型的實(shí)現(xiàn)類(lèi)拘荡,會(huì)賦值給cachedAdaptiveClass
    //目前只有AdaptiveExtensionFactory和AdaptiveCompiler兩個(gè)實(shí)現(xiàn)類(lèi)是被注解了@Adaptive
    //除了ExtensionFactory和Compiler類(lèi)型的擴(kuò)展之外,其他類(lèi)型的擴(kuò)展都是下面動(dòng)態(tài)創(chuàng)建的的實(shí)現(xiàn)
    getExtensionClasses();
    //加載完所有的實(shí)現(xiàn)之后撬陵,發(fā)現(xiàn)有cachedAdaptiveClass不為空
    //也就是說(shuō)當(dāng)前獲取的自適應(yīng)實(shí)現(xiàn)類(lèi)是AdaptiveExtensionFactory或者是AdaptiveCompiler珊皿,就直接返回,這兩個(gè)類(lèi)是特殊用處的巨税,不用代碼生成蟋定,而是現(xiàn)成的代碼
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    //沒(méi)有找到Adaptive類(lèi)型的實(shí)現(xiàn),動(dòng)態(tài)創(chuàng)建一個(gè)
    //比如Protocol的實(shí)現(xiàn)類(lèi)草添,沒(méi)有任何一個(gè)實(shí)現(xiàn)是用@Adaptive來(lái)注解的驶兜,只有Protocol接口的方法是有注解的
    //這時(shí)候就需要來(lái)動(dòng)態(tài)的生成了,也就是生成Protocol$Adaptive
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

加載擴(kuò)展類(lèi)實(shí)現(xiàn)

先看下getExtensionClasses()這個(gè)方法远寸,加載所有的擴(kuò)展類(lèi)的實(shí)現(xiàn):

private Map<String, Class<?>> getExtensionClasses() {
    //從緩存中獲取促王,cachedClasses也是一個(gè)Holder,Holder這里持有的是一個(gè)Map而晒,key是擴(kuò)展點(diǎn)實(shí)現(xiàn)名,value是擴(kuò)展點(diǎn)實(shí)現(xiàn)類(lèi)
    //這里會(huì)存放當(dāng)前擴(kuò)展點(diǎn)類(lèi)型的所有的擴(kuò)展點(diǎn)的實(shí)現(xiàn)類(lèi)
    //這里以Protocol為例阅畴,就是會(huì)存放Protocol的所有實(shí)現(xiàn)類(lèi)
    //比如key為dubbo倡怎,value為com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
    //cachedClasses擴(kuò)展點(diǎn)實(shí)現(xiàn)名稱(chēng)對(duì)應(yīng)的實(shí)現(xiàn)類(lèi)
    Map<String, Class<?>> classes = cachedClasses.get();
    //如果為null,說(shuō)明沒(méi)有被加載過(guò),就會(huì)進(jìn)行加載监署,而且加載就只會(huì)進(jìn)行這一次
    if (classes == null) {
        synchronized (cachedClasses) {
            classes = cachedClasses.get();
            if (classes == null) {
                //如果沒(méi)有加載過(guò)Extension的實(shí)現(xiàn)颤专,進(jìn)行掃描加載,完成后緩存起來(lái)
                //每個(gè)擴(kuò)展點(diǎn)钠乏,其實(shí)現(xiàn)的加載只會(huì)這執(zhí)行一次
                classes = loadExtensionClasses();
                cachedClasses.set(classes);
            }
        }
    }
    return classes;
}

看下loadExtensionClasses()方法栖秕,這個(gè)方法中加載擴(kuò)展點(diǎn)的實(shí)現(xiàn)類(lèi):

private Map<String, Class<?>> loadExtensionClasses() {
    final SPI defaultAnnotation = type.getAnnotation(SPI.class);
    if(defaultAnnotation != null) {
        //當(dāng)前Extension的默認(rèn)實(shí)現(xiàn)名字
        //比如說(shuō)Protocol接口,注解是@SPI("dubbo")
        //這里dubbo就是默認(rèn)的值
        String value = defaultAnnotation.value();
        //只能有一個(gè)默認(rèn)的名字晓避,如果多了簇捍,誰(shuí)也不知道該用哪一個(gè)實(shí)現(xiàn)了。
        if(value != null && (value = value.trim()).length() > 0) {
            String[] names = NAME_SEPARATOR.split(value);
            if(names.length > 1) {
                throw new IllegalStateException();
            }
            //默認(rèn)的名字保存起來(lái)
            if(names.length == 1) cachedDefaultName = names[0];
        }
    }

    //下面就開(kāi)始從配置文件中加載擴(kuò)展實(shí)現(xiàn)類(lèi)
    Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
    //從META-INF/dubbo/internal目錄下加載
    loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
    //從META-INF/dubbo/目錄下加載
    loadFile(extensionClasses, DUBBO_DIRECTORY);
    //從META-INF/services/下加載
    loadFile(extensionClasses, SERVICES_DIRECTORY);
    return extensionClasses;
}

從各個(gè)位置的配置文件中加載實(shí)現(xiàn)類(lèi)俏拱,對(duì)于Protocol來(lái)說(shuō)加載的文件是以com.alibaba.dubbo.rpc.Protocol為名稱(chēng)的文件暑塑,文件的內(nèi)容是(有好幾個(gè)同名的配置文件,這里直接把內(nèi)容全部寫(xiě)在了一起):

registry=com.alibaba.dubbo.registry.integration.RegistryProtocol

filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol

dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol

hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol

com.alibaba.dubbo.rpc.protocol.http.HttpProtocol

injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol

memcached=memcom.alibaba.dubbo.rpc.protocol.memcached.MemcachedProtocol

redis=com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol

rmi=com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol

thrift=com.alibaba.dubbo.rpc.protocol.thrift.ThriftProtocol

com.alibaba.dubbo.rpc.protocol.webservice.WebServiceProtocol

看下loadFile()方法:

private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
    //配置文件的名稱(chēng)
    //這里type是擴(kuò)展類(lèi)锅必,比如com.alibaba.dubbo.rpc.Protocol類(lèi)
    String fileName = dir + type.getName();
    try {
        Enumeration<java.net.URL> urls;
        //獲取類(lèi)加載器
        ClassLoader classLoader = findClassLoader();
        //獲取對(duì)應(yīng)配置文件名的所有的文件
        if (classLoader != null) {
            urls = classLoader.getResources(fileName);
        } else {
            urls = ClassLoader.getSystemResources(fileName);
        }
        if (urls != null) {
            //遍歷文件進(jìn)行處理
            while (urls.hasMoreElements()) {
                //配置文件路徑
                java.net.URL url = urls.nextElement();
                try {
                    BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
                    try {
                        String line = null;
                        //每次處理一行
                        while ((line = reader.readLine()) != null) {
                            //#號(hào)以后的為注釋
                            final int ci = line.indexOf('#');
                            //注釋去掉
                            if (ci >= 0) line = line.substring(0, ci);
                            line = line.trim();
                            if (line.length() > 0) {
                                try {
                                    String name = null;
                                    //=號(hào)之前的為擴(kuò)展名字事格,后面的為擴(kuò)展類(lèi)實(shí)現(xiàn)的全限定名
                                    int i = line.indexOf('=');
                                    if (i > 0) {
                                        name = line.substring(0, i).trim();
                                        line = line.substring(i + 1).trim();
                                    }
                                    if (line.length() > 0) {
                                        //加載擴(kuò)展類(lèi)的實(shí)現(xiàn)
                                        Class<?> clazz = Class.forName(line, true, classLoader);
                                        //查看類(lèi)型是否匹配
                                        //type是Protocol接口
                                        //clazz就是Protocol的各個(gè)實(shí)現(xiàn)類(lèi)
                                        if (! type.isAssignableFrom(clazz)) {
                                            throw new IllegalStateException();
                                        }
                                        //如果實(shí)現(xiàn)類(lèi)是@Adaptive類(lèi)型的,會(huì)賦值給cachedAdaptiveClass搞隐,這個(gè)用來(lái)存放被@Adaptive注解的實(shí)現(xiàn)類(lèi)
                                        if (clazz.isAnnotationPresent(Adaptive.class)) {
                                            if(cachedAdaptiveClass == null) {
                                                cachedAdaptiveClass = clazz;
                                            } else if (! cachedAdaptiveClass.equals(clazz)) {
                                                throw new IllegalStateException();
                                            }
                                        } else {//不是@Adaptice類(lèi)型的類(lèi)驹愚,就是沒(méi)有注解@Adaptive的實(shí)現(xiàn)類(lèi)
                                            try {//判斷是否是wrapper類(lèi)型
                                                //如果得到的實(shí)現(xiàn)類(lèi)的構(gòu)造方法中的參數(shù)是擴(kuò)展點(diǎn)類(lèi)型的,就是一個(gè)Wrapper類(lèi)
                                                //比如ProtocolFilterWrapper劣纲,實(shí)現(xiàn)了Protocol類(lèi)逢捺,
                                                //而它的構(gòu)造方法是這樣public ProtocolFilterWrapper(Protocol protocol)
                                                //就說(shuō)明這個(gè)類(lèi)是一個(gè)包裝類(lèi)
                                                clazz.getConstructor(type);
                                                //cachedWrapperClasses用來(lái)存放當(dāng)前擴(kuò)展點(diǎn)實(shí)現(xiàn)類(lèi)中的包裝類(lèi)
                                                Set<Class<?>> wrappers = cachedWrapperClasses;
                                                if (wrappers == null) {
                                                    cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
                                                    wrappers = cachedWrapperClasses;
                                                }
                                                wrappers.add(clazz);
                                            } catch (NoSuchMethodException e) {
                                            //沒(méi)有上面提到的構(gòu)造器,則說(shuō)明不是wrapper類(lèi)型
                                                //獲取無(wú)參構(gòu)造
                                                clazz.getConstructor();
                                                //沒(méi)有名字味廊,就是配置文件中沒(méi)有xxx=xxxx.com.xxx這種
                                                if (name == null || name.length() == 0) {
                                                    //去找@Extension注解中配置的值
                                                    name = findAnnotationName(clazz);
                                                    //如果還沒(méi)找到名字蒸甜,從類(lèi)名中獲取
                                                    if (name == null || name.length() == 0) {
                                                        //比如clazz是DubboProtocol,type是Protocol
                                                        //這里得到的name就是dubbo
                                                        if (clazz.getSimpleName().length() > type.getSimpleName().length()
                                                                && clazz.getSimpleName().endsWith(type.getSimpleName())) {
                                                            name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();
                                                        } else {
                                                            throw new IllegalStateException(");
                                                        }
                                                    }
                                                }
                                                //有可能配置了多個(gè)名字
                                                String[] names = NAME_SEPARATOR.split(name);
                                                if (names != null && names.length > 0) {
                                                    //是否是Active類(lèi)型的類(lèi)
                                                    Activate activate = clazz.getAnnotation(Activate.class);
                                                    if (activate != null) {
                                                        //第一個(gè)名字作為鍵余佛,放進(jìn)cachedActivates這個(gè)map中緩存
                                                        cachedActivates.put(names[0], activate);
                                                    }
                                                    for (String n : names) {
                                                        if (! cachedNames.containsKey(clazz)) {
                                                            //放入Extension實(shí)現(xiàn)類(lèi)與名稱(chēng)映射的緩存中去柠新,每個(gè)class只對(duì)應(yīng)第一個(gè)名稱(chēng)有效
                                                            cachedNames.put(clazz, n);
                                                        }
                                                        Class<?> c = extensionClasses.get(n);
                                                        if (c == null) {
                                                            //放入到extensionClasses緩存中去,多個(gè)name可能對(duì)應(yīng)一份extensionClasses
                                                            extensionClasses.put(n, clazz);
                                                        } else if (c != clazz) {
                                                            throw new IllegalStateException();
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                } catch (Throwable t) { }
                            }
                        } // end of while read lines
                    } finally {
                        reader.close();
                    }
                } catch (Throwable t) { }
            } // end of while urls
        }
    } catch (Throwable t) { }
}

到這里加載當(dāng)前Extension的所有實(shí)現(xiàn)就已經(jīng)完成了辉巡,繼續(xù)返回getAdaptiveExtensionClass中恨憎,在調(diào)用完getExtensionClasses()之后,會(huì)首先檢查是不是已經(jīng)有@Adaptive注解的類(lèi)被解析并加入到緩存中了郊楣,如果有就直接返回憔恳,這里的cachedAdaptiveClass中現(xiàn)在只能是AdaptiveExtensionFactory或者AdaptiveCompiler中的一個(gè),如果沒(méi)有净蚤,說(shuō)明是一個(gè)普通擴(kuò)展點(diǎn)钥组,就動(dòng)態(tài)創(chuàng)建一個(gè),比如會(huì)創(chuàng)建一個(gè)Protocol$Adaptive今瀑。

創(chuàng)建自適應(yīng)擴(kuò)展類(lèi)的代碼

看下createAdaptiveExtensionClass()這個(gè)方法程梦,用來(lái)動(dòng)態(tài)的創(chuàng)建自適應(yīng)擴(kuò)展類(lèi):

private Class<?> createAdaptiveExtensionClass() {
    //組裝自適應(yīng)擴(kuò)展點(diǎn)類(lèi)的代碼
    String code = createAdaptiveExtensionClassCode();
    //獲取到應(yīng)用的類(lèi)加載器
    ClassLoader classLoader = findClassLoader();
    //獲取編譯器
    //dubbo默認(rèn)使用javassist
    //這里還是使用擴(kuò)展點(diǎn)機(jī)制來(lái)找具體的Compiler的實(shí)現(xiàn)
    //現(xiàn)在就知道cachedAdaptiveClass是啥意思了点把,如果沒(méi)有AdaptiveExtensionFactory和AdaptiveCompiler這兩個(gè)類(lèi),這里又要去走加載流程然后來(lái)生成擴(kuò)展點(diǎn)類(lèi)的代碼屿附,不就死循環(huán)了么郎逃。
    //這里解析Compiler的實(shí)現(xiàn)類(lèi)的時(shí)候,會(huì)在getAdaptiveExtensionClass中直接返回
    //可以查看下AdaptiveCompiler這個(gè)類(lèi)挺份,如果我們沒(méi)有指定褒翰,默認(rèn)使用javassist
    //這里Compiler是JavassistCompiler實(shí)例
    com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    //將代碼轉(zhuǎn)換成Class
    return compiler.compile(code, classLoader);
}

接著看下createAdaptiveExtensionClassCode()方法,用來(lái)組裝自適應(yīng)擴(kuò)展類(lèi)的代碼(拼寫(xiě)源碼匀泊,代碼比較長(zhǎng)不在列出)优训,這里列出生成的Protocol$Adaptive

import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
  public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws java.lang.Class {
    if (arg1 == null) throw new IllegalArgumentException("url == null");

    com.alibaba.dubbo.common.URL url = arg1;
    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.refer(arg0, arg1);
  }
  
  public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {
    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);
  }
  
  public void destroy() {
    throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
  }
  
  public int getDefaultPort() {
    throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
  }
}

其他具體的擴(kuò)展點(diǎn)的生成也類(lèi)似。在生成完代碼之后探赫,是找到ClassLoader型宙,然后獲取到Compiler的自適應(yīng)實(shí)現(xiàn),這里得到的就是AdaptiveCompiler伦吠,最后調(diào)用compiler.compile(code, classLoader);來(lái)編譯上面生成的類(lèi)并返回妆兑,先進(jìn)入AdaptiveCompiler的compile方法:

public Class<?> compile(String code, ClassLoader classLoader) {
    Compiler compiler;
    //得到一個(gè)ExtensionLoader
    ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
    //默認(rèn)的Compiler名字
    String name = DEFAULT_COMPILER; // copy reference
    //有指定了Compiler名字,就使用指定的名字來(lái)找到Compiler實(shí)現(xiàn)類(lèi)
    if (name != null && name.length() > 0) {
        compiler = loader.getExtension(name);
    } else {//沒(méi)有指定Compiler名字毛仪,就查找默認(rèn)的Compiler的實(shí)現(xiàn)類(lèi)
        compiler = loader.getDefaultExtension();
    }
    //調(diào)用具體的實(shí)現(xiàn)類(lèi)來(lái)進(jìn)行編譯
    return compiler.compile(code, classLoader);
}

獲取指定名字的擴(kuò)展

先看下根據(jù)具體的名字來(lái)獲取擴(kuò)展的實(shí)現(xiàn)類(lèi)loader.getExtension(name);搁嗓,loader是ExtensionLoader<Compiler>類(lèi)型的。這里就是比Java的SPI要方便的地方箱靴,Java的SPI只能通過(guò)遍歷所有的實(shí)現(xiàn)類(lèi)來(lái)查找腺逛,而dubbo能夠指定一個(gè)名字查找。代碼如下:

public T getExtension(String name) {
    if (name == null || name.length() == 0)
        throw new IllegalArgumentException("Extension name == null");
    //如果name指定為true衡怀,則獲取默認(rèn)實(shí)現(xiàn)
    if ("true".equals(name)) {
        //默認(rèn)實(shí)現(xiàn)查找在下面解析
        return getDefaultExtension();
    }
    //先從緩存獲取Holder棍矛,cachedInstance是一個(gè)ConcurrentHashMap,鍵是擴(kuò)展的name抛杨,值是一個(gè)持有name對(duì)應(yīng)的實(shí)現(xiàn)類(lèi)實(shí)例的Holder够委。
    Holder<Object> holder = cachedInstances.get(name);
    //如果當(dāng)前name對(duì)應(yīng)的Holder不存在,就創(chuàng)建一個(gè)怖现,添加進(jìn)map中
    if (holder == null) {
        cachedInstances.putIfAbsent(name, new Holder<Object>());
        holder = cachedInstances.get(name);
    }
    //從Holder中獲取保存的實(shí)例
    Object instance = holder.get();
    //不存在茁帽,就需要根據(jù)這個(gè)name找到實(shí)現(xiàn)類(lèi),實(shí)例化一個(gè)
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                //緩存不存在屈嗤,創(chuàng)建實(shí)例
                instance = createExtension(name);
                //加入緩存
                holder.set(instance);
            }
        }
    }
    //存在潘拨,就直接返回
    return (T) instance;
}

創(chuàng)建擴(kuò)展實(shí)例,createExtension(name);

private T createExtension(String name) {
    //getExtensionClasses加載當(dāng)前Extension的所有實(shí)現(xiàn)
    //上面已經(jīng)解析過(guò)饶号,返回的是一個(gè)Map铁追,鍵是name,值是name對(duì)應(yīng)的Class
    //根據(jù)name查找對(duì)應(yīng)的Class
    Class<?> clazz = getExtensionClasses().get(name);
    //如果這時(shí)候class還不存在茫船,說(shuō)明在所有的配置文件中都沒(méi)找到定義脂信,拋異常
    if (clazz == null) {
        throw findException(name);
    }
    try {
        //從已創(chuàng)建實(shí)例緩存中獲取
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        //不存在的話就創(chuàng)建一個(gè)新實(shí)例癣蟋,加入到緩存中去
        if (instance == null) {
            EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        //屬性注入
        injectExtension(instance);
        //Wrapper的包裝
        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) { }
}

有關(guān)屬性注入和Wrapper的包裝,下面再講狰闪。到這里Compiler就能獲得到一個(gè)指定name的具體實(shí)現(xiàn)類(lèi)的實(shí)例了,然后就是調(diào)用實(shí)例的compile()方法對(duì)生成的代碼進(jìn)行編譯濒生。

獲取默認(rèn)擴(kuò)展實(shí)現(xiàn)

如果在AdaptiveCompiler中沒(méi)有找到指定的名字埋泵,就會(huì)找默認(rèn)的擴(kuò)展實(shí)現(xiàn)loader.getDefaultExtension();

public T getDefaultExtension() {
    //首先還是先去加載所有的擴(kuò)展實(shí)現(xiàn)
    //加載的時(shí)候會(huì)設(shè)置默認(rèn)的名字cachedDefaultName,這個(gè)名字是在@SPI中指定的罪治,比如Compiler就指定了@SPI("javassist")丽声,所以這里是javassist
    getExtensionClasses();
    if(null == cachedDefaultName || cachedDefaultName.length() == 0
            || "true".equals(cachedDefaultName)) {
        return null;
    }
    //根據(jù)javassist這個(gè)名字去查找擴(kuò)展實(shí)現(xiàn)
    //具體的過(guò)程上面已經(jīng)解析過(guò)了
    return getExtension(cachedDefaultName);
}

關(guān)于javassist編譯Class的過(guò)程暫先不說(shuō)明。我們接著流程看:

 private T createAdaptiveExtension() {
    try {
        //先通過(guò)getAdaptiveExtensionClass獲取AdaptiveExtensionClass(在上面這一步已經(jīng)解析了觉义,獲得到了一個(gè)自適應(yīng)實(shí)現(xiàn)類(lèi)的Class)
        //然后獲取其實(shí)例雁社,newInstance進(jìn)行實(shí)例
        //最后進(jìn)行注入處理injectExtension
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) { }
}

擴(kuò)展點(diǎn)注入

接下來(lái)就是有關(guān)擴(kuò)展點(diǎn)的注入的問(wèn)題了,injectExtension晒骇,關(guān)于注入的解釋查看最上面擴(kuò)展點(diǎn)自動(dòng)裝配(IOC)的說(shuō)明霉撵,injectExtension方法:

//這里的實(shí)例是Xxxx$Adaptive
private T injectExtension(T instance) {
    try {
        //關(guān)于objectFactory的來(lái)路,先看下面的解析
        //這里的objectFactory是AdaptiveExtensionFactory
        if (objectFactory != null) {
            //遍歷擴(kuò)展實(shí)現(xiàn)類(lèi)實(shí)例的方法
            for (Method method : instance.getClass().getMethods()) {
                //只處理set方法
                //set開(kāi)頭洪囤,只有一個(gè)參數(shù)徒坡,public
                if (method.getName().startsWith("set")
                        && method.getParameterTypes().length == 1
                        && Modifier.isPublic(method.getModifiers())) {
                    //set方法參數(shù)類(lèi)型
                    Class<?> pt = method.getParameterTypes()[0];
                    try {
                        //setter方法對(duì)應(yīng)的屬性名
                        String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                        //根據(jù)類(lèi)型和名稱(chēng)信息從ExtensionFactory中獲取
                        //比如在某個(gè)擴(kuò)展實(shí)現(xiàn)類(lèi)中會(huì)有setProtocol(Protocol protocol)這樣的set方法
                        //這里pt就是Protocol,property就是protocol
                        //AdaptiveExtensionFactory就會(huì)根據(jù)這兩個(gè)參數(shù)去查找對(duì)應(yīng)的擴(kuò)展實(shí)現(xiàn)類(lèi)
                        //這里就會(huì)返回Protocol$Adaptive
                        Object object = objectFactory.getExtension(pt, property);
                        if (object != null) {//說(shuō)明set方法的參數(shù)是擴(kuò)展點(diǎn)類(lèi)型瘤缩,進(jìn)行注入
                            //為set方法注入一個(gè)自適應(yīng)的實(shí)現(xiàn)類(lèi)
                            method.invoke(instance, object);
                        }
                    } catch (Exception e) { }
                }
            }
        }
    } catch (Exception e) {}
    return instance;
}

有關(guān)AdaptiveExtensionFactory中獲取Extension的過(guò)程喇完,會(huì)首先在實(shí)例化的時(shí)候得到ExtensionFactory的具體實(shí)現(xiàn)類(lèi),然后遍歷每個(gè)ExtensionFactory的實(shí)現(xiàn)類(lèi)剥啤,分別在每個(gè)ExtensionFactory的實(shí)現(xiàn)類(lèi)中獲取Extension锦溪。

這里使用SpiExtensionFactory的獲取擴(kuò)展的方法為例,getExtension府怯,也是先判斷給定類(lèi)是否是注解了@SPI的接口刻诊,然后根據(jù)類(lèi)去獲取ExtensionLoader,在使用得到的ExtensionLoader去加載自適應(yīng)擴(kuò)展富腊。

objectFactory的來(lái)歷

objectFactory的來(lái)路坏逢,在ExtensionLoader中有個(gè)私有構(gòu)造器:

//當(dāng)我們調(diào)用getExtensionLoader這個(gè)靜態(tài)方法的時(shí)候,會(huì)觸發(fā)ExtensionLoader類(lèi)的實(shí)例化赘被,會(huì)先初始化靜態(tài)變量和靜態(tài)塊是整,然后是構(gòu)造代碼塊,最后是構(gòu)造器的初始化
private ExtensionLoader(Class<?> type) {
    this.type = type;
    //這里會(huì)獲得一個(gè)AdaptiveExtensionFactory
    //根據(jù)類(lèi)型和名稱(chēng)信息從ExtensionFactory中獲取
    //獲取實(shí)現(xiàn)
    //為什么要使用對(duì)象工廠來(lái)獲取setter方法中對(duì)應(yīng)的實(shí)現(xiàn)民假?
    //不能通過(guò)spi直接獲取自適應(yīng)實(shí)現(xiàn)嗎浮入?比如ExtensionLoader.getExtension(pt);
    //因?yàn)閟etter方法中有可能是一個(gè)spi,也有可能是普通的bean
    //所以此時(shí)不能寫(xiě)死通過(guò)spi獲取羊异,還需要有其他方式來(lái)獲取實(shí)現(xiàn)進(jìn)行注入
    // dubbo中有兩個(gè)實(shí)現(xiàn)事秀,一個(gè)是spi的ExtensionFactory彤断,一個(gè)是spring的ExtensionFactory
    //如果還有其他的,我們可以自定義ExtensionFactory
    //objectFactory是AdaptiveExtensionFactory實(shí)例
    objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

到此為止createAdaptiveExtension方法解析完成易迹,接著返回上層getAdaptiveExtension()方法中宰衙,發(fā)現(xiàn)創(chuàng)建完自適應(yīng)擴(kuò)展實(shí)例之后,就會(huì)加入到cachedAdaptiveInstance緩存起來(lái)睹欲,然后就會(huì)返回給調(diào)用的地方一個(gè)Xxx$Adaptive實(shí)例供炼。

走到這里,下面的代碼就解析完了:

private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

得到擴(kuò)展之后的使用

我們得到了一個(gè)Protocol$Adaptive實(shí)例窘疮,接著就是調(diào)用了袋哼,比如說(shuō)我們要調(diào)用refprotocol.refer(Class<T> type, URL url))方法,由于這里refprotocol是一個(gè)Protocol$Adaptive實(shí)例闸衫,所以就先調(diào)用這個(gè)實(shí)例的refer方法涛贯,這里的實(shí)例的代碼在最上面:

//這里為了好看,代碼做了精簡(jiǎn)蔚出,包名都去掉了
public Invoker refer(Class arg0, URL arg1) throws Class {
    if (arg1 == null) throw new IllegalArgumentException();

    URL url = arg1;
    String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );

    if(extName == null) throw new IllegalStateException();
    
    Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);
    
    return extension.refer(arg0, arg1);
  }

可以看到這里首先根據(jù)url中的參數(shù)獲取擴(kuò)展名字弟翘,如果url中沒(méi)有就使用默認(rèn)的擴(kuò)展名,然后根據(jù)擴(kuò)展名去獲取具體的實(shí)現(xiàn)身冬。關(guān)于getExtension(String name)上面已經(jīng)解析過(guò)一次衅胀,這里再次列出:

public T getExtension(String name) {
    if (name == null || name.length() == 0)
        throw new IllegalArgumentException("Extension name == null");
    //如果name指定為true,則獲取默認(rèn)實(shí)現(xiàn)
    if ("true".equals(name)) {
        //默認(rèn)實(shí)現(xiàn)查找在下面解析
        return getDefaultExtension();
    }
    //先從緩存獲取Holder酥筝,cachedInstance是一個(gè)ConcurrentHashMap滚躯,鍵是擴(kuò)展的name,值是一個(gè)持有name對(duì)應(yīng)的實(shí)現(xiàn)類(lèi)實(shí)例的Holder嘿歌。
    Holder<Object> holder = cachedInstances.get(name);
    //如果當(dāng)前name對(duì)應(yīng)的Holder不存在顶岸,就創(chuàng)建一個(gè)益眉,添加進(jìn)map中
    if (holder == null) {
        cachedInstances.putIfAbsent(name, new Holder<Object>());
        holder = cachedInstances.get(name);
    }
    //從Holder中獲取保存的實(shí)例
    Object instance = holder.get();
    //不存在,就需要根據(jù)這個(gè)name找到實(shí)現(xiàn)類(lèi),實(shí)例化一個(gè)
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                //緩存不存在愈腾,創(chuàng)建實(shí)例
                instance = createExtension(name);
                //加入緩存
                holder.set(instance);
            }
        }
    }
    //存在笔喉,就直接返回
    return (T) instance;
}

創(chuàng)建擴(kuò)展實(shí)例堡僻,createExtension(name);

private T createExtension(String name) {
    //getExtensionClasses加載當(dāng)前Extension的所有實(shí)現(xiàn)
    //上面已經(jīng)解析過(guò)观谦,返回的是一個(gè)Map,鍵是name靴患,值是name對(duì)應(yīng)的Class
    //根據(jù)name查找對(duì)應(yīng)的Class
    //比如name是dubbo仍侥,Class就是com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
    Class<?> clazz = getExtensionClasses().get(name);
    //如果這時(shí)候class還不存在,說(shuō)明在所有的配置文件中都沒(méi)找到定義鸳君,拋異常
    if (clazz == null) {
        throw findException(name);
    }
    try {
        //從已創(chuàng)建實(shí)例緩存中獲取
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        //不存在的話就創(chuàng)建一個(gè)新實(shí)例农渊,加入到緩存中去
        if (instance == null) {
            EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        //這里實(shí)例就是具體實(shí)現(xiàn)的實(shí)例了比如是DubboProtocol的實(shí)例
        //屬性注入,在上面已經(jīng)解析過(guò)了或颊,根據(jù)實(shí)例中的setXxx方法進(jìn)行注入
        injectExtension(instance);
        //Wrapper的包裝
        //cachedWrapperClasses存放著所有的Wrapper類(lèi)
        //cachedWrapperClasses是在加載擴(kuò)展實(shí)現(xiàn)類(lèi)的時(shí)候放進(jìn)去的
        //Wrapper類(lèi)的說(shuō)明在最上面擴(kuò)展點(diǎn)自動(dòng)包裝(AOP)
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (wrapperClasses != null && wrapperClasses.size() > 0) {
            for (Class<?> wrapperClass : wrapperClasses) {
                //比如在包裝之前的instance是DubboProtocol實(shí)例
                //先使用構(gòu)造器來(lái)實(shí)例化當(dāng)前的包裝類(lèi)
                //包裝類(lèi)中就已經(jīng)包含了我們的DubboProtocol實(shí)例
                //然后對(duì)包裝類(lèi)進(jìn)行injectExtension注入砸紊,注入過(guò)程在上面
                //最后返回的Instance就是包裝類(lèi)的實(shí)例传于。
                instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
            }
        }
        //這里返回的是經(jīng)過(guò)所有的包裝類(lèi)包裝之后的實(shí)例
        return instance;
    } catch (Throwable t) { }
}

獲取的Extension是經(jīng)過(guò)層層包裝的擴(kuò)展實(shí)現(xiàn),然后就是調(diào)用經(jīng)過(guò)包裝的refer方法了醉顽,這就到了具體的實(shí)現(xiàn)中的方法了沼溜。

到此為止調(diào)用refprotocol.refer(Class<T> type, URL url))方法的過(guò)程也解析完了。

關(guān)于getActivateExtension方法的解析徽鼎,等下再添加盛末。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市否淤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌棠隐,老刑警劉巖石抡,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異助泽,居然都是意外死亡啰扛,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)嗡贺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)隐解,“玉大人,你說(shuō)我怎么就攤上這事诫睬∩访#” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵摄凡,是天一觀的道長(zhǎng)续徽。 經(jīng)常有香客問(wèn)我,道長(zhǎng)亲澡,這世上最難降的妖魔是什么钦扭? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮床绪,結(jié)果婚禮上客情,老公的妹妹穿的比我還像新娘。我一直安慰自己癞己,他們只是感情好膀斋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著末秃,像睡著了一般概页。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上练慕,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天惰匙,我揣著相機(jī)與錄音技掏,去河邊找鬼。 笑死项鬼,一個(gè)胖子當(dāng)著我的面吹牛哑梳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播绘盟,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼鸠真,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了龄毡?” 一聲冷哼從身側(cè)響起吠卷,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎沦零,沒(méi)想到半個(gè)月后祭隔,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡路操,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年疾渴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屯仗。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡搞坝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出魁袜,到底是詐尸還是另有隱情桩撮,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布慌核,位于F島的核電站距境,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏垮卓。R本人自食惡果不足惜垫桂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望粟按。 院中可真熱鬧诬滩,春花似錦、人聲如沸灭将。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)庙曙。三九已至空镜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吴攒。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工张抄, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人洼怔。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓署惯,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親镣隶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子极谊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容