1. 程式人生 > >dubbo原始碼(章節二) -- dubbo的Ioc

dubbo原始碼(章節二) -- dubbo的Ioc

上一篇主要分析了extensionLoader的獲取,以及獲取extension的第一種方式,即通過裝飾類或者動態代理的方式,今天我們首先從獲取extension的第二種方式說起。

/**
 * Find the extension with the given name.
 */
getExtension(String name)

下面討論getExtension(String name)

先從這個方法的程式碼跟蹤開始,

1 public T getExtension(String name) {2   Holder<Object> holder = cachedInstances.get(name);
3   ......4   Object instance = holder.get(); 5   ...... 6   instance = createExtension(name);7   return (T) instance; 8 }

這裡省略了比較多的內容,有了前面一篇跟蹤程式碼的經驗,快取的具體使用就不再貼出來了,我們只看主要邏輯即可,跟進去看createExtension(name),

 1 private T createExtension(String name) {
 2   Class<?> clazz = getExtensionClasses().get(name);
3   T instance = clazz.newInstance(); 4 5   injectExtension(instance); 6   Set<Class<?>> wrapperClasses = cachedWrapperClasses; 7   for (Class<?> wrapperClass : wrapperClasses) { 8     instance = 9     injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
10   }11   return instance; 12 }

這裡先是通過getExtensionClasses().get(name)拿到一個class物件,getExtensionClasses()方法上一篇說過了,它最終所賦值的是type擴充套件的所有實現中,既沒有@Adaptive註解,也不包含type型別的構造器(這一類擴充套件實現我們稱之為包裝類)的那些實現類,並且被快取在cachedClasses map中,map的key即為實現類在dubbo spi配置檔案中的類名,這裡提一個細節,如果在cachedClasses中沒有拿到key為name對應的value,這裡就會丟擲異常,那也就是說,getExtension(name)這種方式,只能用來獲取既非@Adaptive註解,又非包裝類的那些類的實現,因為只有這樣的類才會被快取在cachedClasses中。這裡拿到這個類並例項化一個Instance,然後呼叫了方法injectExtension(instance),這個方法看名字就知道了,inject既就是注射的意思,這裡就將實現dubbo的依賴注入,現在來看這個方法的實現,

 1 private T injectExtension(T instance) {  2   for (Method method : instance.getClass().getMethods()) {
 3     if (method.getName().startsWith("set")
 4         && method.getParameterTypes().length == 1
 5         && Modifier.isPublic(method.getModifiers())) {
 6       Class<?> pt = method.getParameterTypes()[0];
 7                           8       String property = method.getName().length() > 3 ?  9         method.getName().substring(3, 4).toLowerCase() + 10         method.getName().substring(4) : "";
11       Object object = objectFactory.getExtension(pt, property);12       method.invoke(instance, object);13     }14   }15   return instance;
16 }

把這個class的所有方法都提取出來,然後做了一系列的判斷,這就是要通過setter方法為物件注入屬性了。被注入的object是通過objectFactory.getExtension(...)得到的,回憶一下上一篇說過,每個extension物件都會有一個objectFactory,objectFactory就是一個AdaptiveExtensionFactory,它的作用是:為dubbo的Ioc提供所有物件。所以這裡我們看到,setter方法要注入的屬性值,是通過這個擴充套件的objectFactory拿到的,我們跟進去看一下它的實現,

1 public <T> T getExtension(Class<T> type, String name) {
2   for (ExtensionFactory factory : factories) {
3     T extension = factory.getExtension(type, name);4     return extension;5   }6 }

這裡會遍歷factories,每個元素都是ExtensionFactory的一個非Adaptive實現,我們在上一篇已經看到了,ExtensionFactory的非Adpative實現,最終被放入factories中的,是SpiExtensionFactory,不過實際上在另外的包裡還有一個ExtensionFactory的非Adaptive實現類:SpringExtensionFactory,這裡我們不妨把SpiExtensionFactory和SpringExtensionFactory同時拿來分析,可以看到只要這裡getExtension(type,name)返回非空,就直接返回所獲取的這個值,我們依次看下這兩個實現分別是怎麼做的,先看SpiExtensionFactory:

1 public <T> T getExtension(Class<T> type, String name) {
2   if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
3     ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
4     if (!loader.getSupportedExtensions().isEmpty()) {
5       return loader.getAdaptiveExtension();
6     }
7   }
8   return null;
9 }

這裡判斷type是不是@SPI標註的註解,如果是,說明這個type是一個dubbo spi擴充套件,那麼就返回它的一個AdaptiveExtension,它為什麼叫SpiExtensionFactory呢?就是獲取Spi擴充套件的一個factory,獲取spi擴充套件的adaptive實現的做法在上一篇裡已經討論過了。也就是說,如果一個擴充套件實現類中需要注入另一個spi擴充套件的實現,那就是通過它的objectFactory裡的SpiExtensionFactory來獲取需要注入的這個擴充套件的Adaptive實現。

那麼如果要注入的物件不是dubbo spi擴充套件呢,這裡不會進入if,就會返回null,我們接下來看SpringExtensionFactory,

 1 public <T> T getExtension(Class<T> type, String name) {
 2   for (ApplicationContext context : contexts) {
 3     if (context.containsBean(name)) {
 4       Object bean = context.getBean(name);  5       return (T) bean;  6     }
 7   }
 8 
 9   for (ApplicationContext context : contexts) {10     return context.getBean(type);11   }
12   return null;
13 }

SpringExtensionFactory就很簡單了,直接從spring的ApplicationContext中嘗試獲取bean,先嚐試通過name獲取,如果by name失敗,再嘗試通過by type來獲取。也就是說,如果一個擴充套件實現類中需要注入一個普通物件(非Spi註解的dubbo擴充套件),那就通過它的objectFactory裡的SpringExtensionFactory來獲取這個要被注入的類物件,當然前提是spring容器中已經注入了這個物件。

ok,通過objectFactory提供的物件,我們完成了extension屬性的注入,不過createExtension方法所做的並不止這些,我們回到injectExtension的呼叫處,即createExtension方法的第五行,接著往下看,程式碼第六行獲取了快取cachedWrapperClasses,上一篇講了,這個變數快取了所有非@Adaptive註解同時包含了帶有本擴充套件型別的構造器方法的那些擴充套件實現類。實際上從這個快取的名字裡就能看出來,Wrapper意指包裝,就是說這裡所包含的類都是包裝類。為了解釋這行程式碼,這裡必須舉一個例子:我們看介面Protocol,

@SPI("dubbo")
public interface Protocol{
  ......  
}

這是一個@Spi註解的dubbo擴充套件介面,考慮它的三個實現,MockProtocol,ProtocolFilterWrapper,ProtocolListenerWrapper,

final public class MockProtocol implements Protocol {
  @Override
  public int getDefaultPort() {
    return 0;
  }
  ......
}
public class ProtocolFilterWrapper implements Protocol {
  private final Protocol protocol;

  public ProtocolFilterWrapper(Protocol protocol) {
    if (protocol == null) {
      throw new IllegalArgumentException(......);
    }
    this.protocol = protocol;
  }
  @Override
  public int getDefaultPort() {
    return protocol.getDefaultPort();
  }  ......
}
public class ProtocolListenerWrapper implements Protocol {
  private final Protocol protocol;

  public ProtocolListenerWrapper(Protocol protocol) {
    if (protocol == null) {
      throw new IllegalArgumentException(......);
    }
    this.protocol = protocol;
  }
  @Override
  public int getDefaultPort() {
    return protocol.getDefaultPort();
  }  ...... 
}

可以看到,這三個類都沒有被@Adaptive註解,其中ProtocolFilterWrapper,ProtocolListenerWrapper都有一個私有屬性Protocol,同時有一個Protocol型別作為入參的構造器,所以在類載入之後,這兩個類都會被放入cachedWrapperClasses快取中,而MockProtocol則既不被@Adaptive註解,也不包含Protocol作為入參的構造器,它在類載入之後會被放入cachedClasses中,所以它是可以被通過第二種獲取擴充套件物件的方式獲取的:

ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("mock");

其中"mock"為dubbo spi配置檔案中該類的name。

ok,當MockProtocol被注入了屬性之後,程式碼獲取了cachedWrapperClasses的值,然後依次取出其中快取的類,初始化它們,並將當前protocol作為引數傳入構造器,同時將返回的protocol賦值給當前instance。所以,如果cachedWrapperClasses中的順序是:ProtocolFilterWrapper,ProtocolListenerWrapper,那麼執行完上述程式碼之後,在createExtension方法的最後一行,我們最終獲取到的instance將不再是MockProtocol的例項,而是ProtocolListenerWrapper的例項,它裡面擁有一個protocol屬性,此protocol將會是ProtocolFilterWrapper的例項,而它裡面還是擁有一個protocol屬性,這個protocol才是我們一開始就拿到的MockProtocol的例項。如下所示;

instance([email protected])
    -->protocol([email protected])
        -->protocol([email protected])

看到這,想必大家就明白了為什麼這一類的實現類,會被稱為包裝類了吧。如果呼叫instance的方法,例如程式碼中貼出來的getDefaultPort(),就將從包裝的最外層開始向內呼叫。

ok,這一篇我們分析了dubbo獲取擴充套件實現的第二種方式,同時分析了dubbo Ioc的原理,總結下dubbo的spi:

  • 要獲取dubbo spi介面的實現,就要先獲取對應的ExtensionLoader,而通過loader獲取實現的方式有兩種。
  • getExtensionClasses()方法會載入配置檔案中配置的該介面的所有實現,並賦值給相應的快取:
    • 介面的所有實現中,要麼存在唯一的一個類被@Adaptive註解,要麼就動態生成一個Adaptive代理類。這個類被快取在cachedAdaptiveClass中,我們稱之為第一種實現類。
    • 介面的所有實現中,如果存在一些實現,沒有被@Adaptive註解,但是包含一個以該介面型別為引數的構造器,稱這種類為第二種實現類或包裝類,它們被快取在cachedWrapperClasses中。
    • 剩下的實現類,既不被@Adaptive註解,也不包含特殊的構造器,我們稱之為第三種實現類,它們被快取在cachedClasses中。
  • getAdaptiveExtension()方法將獲得擴充套件介面的裝飾模式的實現類,這個類有且只有一個。
  • getExtension(name)方法根據配置檔案中的類的name來獲取擴充套件實現類,只有第三種實現類能通過這種方式被獲取,但是如果該介面有包裝類存在,那麼此方法獲取的永遠是被包裝的類。