dubbo spi(續)

以前寫了一篇關於dubbo spi的文章,如今看來後面的代碼交代的不清楚,今天作一點補充。java

http://my.oschina.net/zjItLife/blog/530923編程

dubbo的spi重要的是理解類ExtensionLoader。緩存

這個類的頭部做者就交代了這個類要作什麼app

Dubbo使用的擴展點獲取。this

自動注入關聯擴展點。.net

自動Wrap上擴展點的Wrap類。code

缺省得到的的擴展點是一個Adaptive Instance。blog

那麼到底是怎麼作的咱們不妨來看看他的變量在作什麼。接口

dubbo變量裏面有兩個表示文件夾的變量get

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/";

下文涉及到這三個變量的方法在這裏。

LoadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
    loadFile(extensionClasses, DUBBO_DIRECTORY);
    LoadFile(extensionClasses, SERVICES_DIRECTORY);

也就是說擴展類extensionClasses都是從這三個文件夾裏面獲取的。 接下來的兩個變量

private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();

下文的方法都是從這兩個容器得到ExtensionLoader類和ExtensionLoader類實例

接下來是兩個變量

private final Class<?> type;
 private final ExtensionFactory objectFactory;

一個是該ExtensionLoader類表明的接口類型還有就是生成ExtensionLoader的適配器工廠。

接下來是緩存容器

private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
    
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String,Class<?>>>();

    private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();

    private volatile Class<?> cachedAdaptiveClass = null;

    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();

    private String cachedDefaultName;

    private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
    private volatile Throwable createAdaptiveInstanceError;

    private Set<Class<?>> cachedWrapperClasses;

還有一個容錯容器

private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>();

接下來咱們看看各個方法都作了什麼 一、何時生成了這個ExtensionLoader

dubbo代碼中不少地方都調用了ExtensionLoader類的方法getExtensionLoader。該方法採用單利模式生成ExtensionLoader類的實例。

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
  //首先判斷出入的類是否爲爲空和接口
        if (type == null)
            throw new IllegalArgumentException("Extension type == null");
        if(!type.isInterface()) {
            throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
        }
//判斷是否打了註解@SPI
        if(!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type(" + type + 
                    ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }
        //先從容器中獲取
        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;
    }

    private ExtensionLoader(Class<?> type) {
    //生成的時候將類型傳入,並生成一個類型爲ExtensionFactory的適配器,賦值給objectFactory。
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

二、基於類型的適配器類和擴展類是如何被掃描進來的?

在交代變量的時候咱們發現了掃描上文件夾下的類集合,那麼這些獲取擴展類的方法在方法loadExtensionClasses中

// 此方法已經getExtensionClasses方法同步過。
    private Map<String, Class<?>> loadExtensionClasses() {
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if(defaultAnnotation != null) {
        //獲取@SPI裏面的值,這個值後來用來獲取默認的實現類。
            String value = defaultAnnotation.value();
            if(value != null && (value = value.trim()).length() > 0) {
                String[] names = NAME_SEPARATOR.split(value);
                if(names.length > 1) {
                    throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
                            + ": " + Arrays.toString(names));
                }
                if(names.length == 1) cachedDefaultName = names[0];
            }
        }
        
        Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
        loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
        loadFile(extensionClasses, DUBBO_DIRECTORY);
        loadFile(extensionClasses, SERVICES_DIRECTORY);
        return extensionClasses;
    }

這個類的做用只是將三個文件夾裏面與type一致的名稱和類的映射關係維護到類容器中,咱們仔細看看這個類。

private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
       //獲取文件路徑,由於dubbo的各個配置文件符合文件夾+接口名稱這樣的規則
       
            //1獲取類加載器
            //二、根據類加載器加載文件
            //三、遍歷文件
            //四、將每一行信息按照「=」分隔。
            //4.一、獲取=右面的類。
            //4.二、保證獲取的類是不是type接口的實現類。
            //4.三、若是類標有@Adaptive,則將這個類放置到適配器類的位置。(適配器類只能是一個)
            //4.四、若是不是適配器類,則將其其名稱和類之間的關係維護到容器cachedNames和extensionClasses中
         
    }

三、dubbo spi重要的就是在初期生成了一個適配器類,用作以後找到真正實現類的中轉站。

injectExtension((T) getAdaptiveExtensionClass().newInstance());

一、生成適配器類

二、實例化適配器類

三、若是是工廠類的適配器則injectExtension未作任何事情。若是非工廠類會獲取全部set方法的屬性值,若是工廠類可以查找到相應的適配器類,則將其注入進來。

四、 尋找擴展點,dubbo spi費了半天的勁兒去實例化了一個適配器,做用仍是在生成擴展點起做用。

4.一、獲取缺省的擴展點

上文已經交代,在生成ExtensionLoader實例的時候就找到了一個默認的擴展點名稱,將其放置到了變量cachedDefaultName中。根據這個名稱,咱們能夠生成默認的擴展點實例。

public T getDefaultExtension() {
	    getExtensionClasses();
       if(null == cachedDefaultName || cachedDefaultName.length() == 0
               || "true".equals(cachedDefaultName)) {
           return null;
       }
       return getExtension(cachedDefaultName);
	}

4.二、獲取指定名稱的擴展點

T getExtension(String name)

4.三、獲取指定名稱的擴展類

private Class<?> getExtensionClass(String name)

五、編程方式注入擴展點

5.一、編程方式添加新擴展點

public void addExtension(String name, Class<?> clazz)

5.二、編程方式添加替換已有擴展點

public void replaceExtension(String name, Class<?> clazz)

至此,ExtensionLoader類就交代完了,對於這個類的理解將對咱們深刻理解dubbo源碼有必定幫助。

做者hyssop,請君留言批評指正。

相關文章
相關標籤/搜索