dubbo源碼分析01:SPI機制

1、什麼是SPI

  SPI全稱爲Service Provider Interface,是一種服務發現機制,其本質是將接口實現類的全限定名配置在文件中,並由服務加載器讀取配置文件。這樣能夠在運行時,動態爲該接口替換實現類。
  JDK提供了默認的SPI實現,可是Dubbo並未使用JDK提供的SPI,而是本身封裝了一套。咱們先來經過Dubbo官網給的兩個例子簡單瞭解下JDK和Dubbo的SPI是如何使用的。

1.1.JDK SPI示例

  首先定義一個接口以及它的兩個實現類
 1 public interface Robot {
 2     void sayHello();
 3 }
 4 
 5 public class OptimusPrime implements Robot {
 6     
 7     @Override
 8     public void sayHello() {
 9         System.out.println("Hello, I am Optimus Prime.");
10     }
11 }
12 
13 public class Bumblebee implements Robot {
14 
15     @Override
16     public void sayHello() {
17         System.out.println("Hello, I am Bumblebee.");
18     }
19 }

  接下來,在項目(以一個典型的maven項目爲例)的「resources/META-INF/services」路徑下建立一個文件,名稱爲Robot的全限定名「org.apache.spi.Robot」。文件內容以下:java

org.apache.spi.OptimusPrime
org.apache.spi.Bumblebee

  編寫測試代碼,運行以後能夠看到兩個實現類被加載並調用了sayHello方法(調用結果演示略)。spring

public class JavaSPITest {

    @Test
    public void sayHello() throws Exception {
        ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class);
        System.out.println("Java SPI");
        serviceLoader.forEach(Robot::sayHello);
    }
}

 

1.2.Dubbo SPI示例

  仍然使用JDK SPI示例中的接口和其實現類代碼,不過須要在接口Robot上添加註解@SPI
//使用SPI註解標註的接口
@SPI
public interface Robot {
    void sayHello();
}

  接下來,在項目(以一個典型的maven項目爲例)的「resources/META-INF/dubbo」路徑下建立一個文件,名稱爲Robot的全限定名「org.apache.spi.Robot」。文件內容以下:apache

optimusPrime=org.apache.spi.OptimusPrime
bumblebee=org.apache.spi.Bumblebee

 

  經過測試類進行測試,則會執行相應實現類的sayHello方法:
//測試類
public class DubboSPITest {

    @Test
    public void sayHello() throws Exception {
        //傳入一個標註有@SPI的接口Class,經過getExtensionLoader獲取該SPI接口的ExtensionLoader實例
        ExtensionLoader<Robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class);
        //傳入要獲取的實現類的name(META-INF/dubbo/org.apache.spi.Robot文件下的name),獲取實現類實例
        Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
        optimusPrime.sayHello();
        Robot bumblebee = extensionLoader.getExtension("bumblebee");
        bumblebee.sayHello();
    }
}

  經過與JDK SPI示例的比較,發現Dubbo SPI與JDK SPI最大的不一樣就是Dubbo SPI經過鍵值對的方式進行配置。這樣最大的好處是能夠按需加載指定的實現類(經過name指定)。下面就讓咱們以本例中getExtensionLoader與getExtension兩個方法做爲引子,分析Dubbo SPI的實現源碼。數組

 

2、getExtensionLoader

  該方法根據SPI接口類型建立一個ExtensionLoader實例,即每種類型的SPI接口都有一個相應的ExtensionLoader。代碼以下所示:
/**************************************** 相關字段 ****************************************/

//ExtensionLoader對應的SPI接口類型
private final Class<?> type;

//ExtensionLoader對應的ExtensionFactory實例
private final ExtensionFactory objectFactory;

//ExtensionLoader全局緩存,key爲SPI接口類型,value爲相應的ExtensionLoader實例
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>();

/**************************************** 相關方法 ****************************************/

/**
 * 靜態方法,根據SPI接口類型獲取相應的ExtensionLoader
 */
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    //判斷type是否爲空、是不是接口類型、是否具備@SPI註解
    if (type == null) {
        throw new IllegalArgumentException("Extension type == null");
    }
    if (!type.isInterface()) {
        throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
    }
    if (!withExtensionAnnotation(type)) {
        throw new IllegalArgumentException("Extension type (" + type + ") is not an extension...");
    }
    
    //從ExtensionLoader的緩存中根據SPI接口類型獲取對應的ExtensionLoader實例
    ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    //若緩存沒有該實例,則new一個,而且存放入緩存,key爲type
    if (loader == null) {
        EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
        loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    }
    return loader;
}

/**
 * 私有構造器,對調用者而言,只能經過getExtensionLoader方法獲取ExtensionLoader實例
 */
private ExtensionLoader(Class<?> type) {
    //保存該ExtensionLoader的SPI接口類型信息
    this.type = type; 
    //若SPI接口類型爲ExtensionFactory,則不設置字段ExtensionFactory,
    //不然須要設置一個ExtensionFactory。具體的獲取邏輯咱們後面會講到
    objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

 

3、getExtension

  該方法用於獲取ExtensionLoader對應的SPI接口的實現類實例,name用於指定須要獲取的實現類類型。在後面,咱們將SPI接口實現類統一稱爲擴展類。
 
/**************************************** 實例緩存相關字段 ****************************************/

//所有SPI接口的擴展類實例緩存,key爲擴展類Class(每個SPI接口的每個實現類的Class都不一樣)
//value爲對應的擴展類實例。注意該緩存要與另一個相似的緩存cachedInstances區分開。
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>();

//每一個ExtensionLoader對應的SPI接口的擴展類實例緩存,
//key爲擴展類的name,value爲Holder對象,其持有/維護擴展類的實例。
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();

/**************************************** Class緩存相關字段 ****************************************/

//每一個ExtensionLoader對應的SPI接口的擴展類Class緩存,key爲擴展類的name,value爲擴展類Class
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();

//SPI接口的擴展類中具備@Adaptive註解的擴展類Class緩存
private volatile Class<?> cachedAdaptiveClass = null;

//SPI接口的擴展類中被斷定爲具備包裝功能的擴展類Class緩存
private Set<Class<?>> cachedWrapperClasses;

//SPI接口的擴展類中具備@Activate註解的緩存,key是擴展類names[0],value爲@Activate的Class
private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>();

/**************************************** name緩存相關字段 ****************************************/

//SPI接口的擴展類的name緩存,key爲擴展類的Class,value爲擴展類的names[0]
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();

//SPI接口的默認擴展類的name
private String cachedDefaultName;

/**************************************** 其餘相關字段 ****************************************/

//ExtensionLoader對應的SPI接口Class
private final Class<?> type;

/**************************************** 相關方法 ****************************************/

/**
 * 獲取ExtensionLoader對應的SPI接口的擴展類實例
 */
public T getExtension(String name) {
    //檢查擴展類的name
    if (StringUtils.isEmpty(name)) {
        throw new IllegalArgumentException("Extension name == null");
    }
    //若是傳入的name值爲true,則獲取默認的SPI接口擴展類實例
    if ("true".equals(name)) {
        return getDefaultExtension();
    }
    //getOrCreateHolder方法比較簡單,它從緩存「cachedInstances」中獲取該name對應的Holder實例,
    //Holder是一個「持有類」,其可能持有擴展類實例
    Holder<Object> holder = getOrCreateHolder(name);
    Object instance = holder.get();
    //若是未獲取到實例,在監視器鎖中進行第二次獲取與建立
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                //建立name對應的擴展類實例,並緩存到cachedInstances中
                instance = createExtension(name);
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}

/**
 * 獲取默認的擴展類實例
 */
public T getDefaultExtension() {
    //調用getExtensionClasses方法獲取SPI接口配置的擴展類信息,返回結果爲一個Map<String, Class>,
    //其中key爲擴展類name,value爲該name對應的擴展類Class。
    getExtensionClasses(); 
    
    //若是SPI接口默認擴展類name爲空或者爲true,則默認的擴展類實例爲null
    if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) {
        return null;
    }
    //不然調用getExtension根據默認的擴展類name去獲取實例
    return getExtension(cachedDefaultName);
}

/**
 * 建立一個擴展類的實例
 */
private T createExtension(String name) {
    //調用getExtensionClasses方法,從返回結果中獲取name對應的擴展類Class,若是Class爲null,則拋出異常
    Class<?> clazz = getExtensionClasses().get(name);  
    if (clazz == null) {
        throw findException(name);
    }
    try {
        //從緩存「EXTENSION_INSTANCES」中根據擴展類Class獲取實例
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
            //緩存未命中則經過反射建立一個實例,並存入緩存
            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        //向這個擴展類實例注入其所須要的屬性(以setXXX爲準),該方法比較複雜,咱們後面會進行分析
        injectExtension(instance);
        //獲取SPI接口擴展類中具備包裝功能的擴展類Class緩存,而後對instance進行層層包裝(裝飾器模式),
        //對每次包裝出來的新實例進行屬性注入,所有包裝完成後讓instance指向最終的包裝結果實例 
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (CollectionUtils.isNotEmpty(wrapperClasses)) {
            for (Class<?> wrapperClass : wrapperClasses) {
                instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
            }
        }
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException("Extension instance ... couldn't be instantiated: ");
    }
}

/**
 * 獲取SPI接口配置的擴展類信息
 */
private Map<String, Class<?>> getExtensionClasses() {
    //從緩存「cachedClasses」中獲取已加載的擴展類
    Map<String, Class<?>> classes = cachedClasses.get();
    if (classes == null) {
        synchronized (cachedClasses) {
            classes = cachedClasses.get();
            if (classes == null) {
                //緩存爲空,調用loadExtensionClasses方法加載SPI接口配置的擴展類信息,並緩存結果
                classes = loadExtensionClasses();
                cachedClasses.set(classes);
            }
        }
    }
    return classes;
}

/**
 * 加載SPI接口配置的擴展類信息
 */
private Map<String, Class<?>> loadExtensionClasses() {
    //獲取SPI接口默認擴展類的name並進行緩存
    cacheDefaultExtensionName();
    
    //讀取並解析SPI接口的配置文件,去幾個固定的目錄下讀取
    //(1):META-INF/services/SPI接口全限定類名(或SPI接口全限定類名替換org.apache爲com.alibaba)
    //(2):META-INF/dubbo/SPI接口全限定類名(或SPI接口全限定類名替換org.apache爲com.alibaba)
    //(3):META-INF/dubbo/internal/SPI接口全限定類名(或SPI接口全限定類名替換org.apache爲com.alibaba)
    Map<String, Class<?>> 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;
}

/**
 * 獲取SPI接口默認擴展類的name並進行緩存
 */
private void cacheDefaultExtensionName() {
    //獲取ExtensionLoader對應的SPI接口上的SPI註解
    final SPI defaultAnnotation = type.getAnnotation(SPI.class);
    if (defaultAnnotation != null) {
        //獲取SPI註解的value值並進行校驗
        String value = defaultAnnotation.value();
        if ((value = value.trim()).length() > 0) {
            String[] names = NAME_SEPARATOR.split(value);
            if (names.length > 1) {
                throw new IllegalStateException("More than 1 default extension name ...");
            }
            if (names.length == 1) {
                //將其做爲SPI接口默認擴展類的name並進行緩存
                cachedDefaultName = names[0];
            }
        }
    }
}

/**
 * 加載SPI接口的配置文件
 */
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
    String fileName = dir + type;
    try {
        Enumeration<java.net.URL> urls;
        //獲取並使用類加載器去加載文件(同名文件進行內容合併)
        ClassLoader classLoader = findClassLoader();
        if (classLoader != null) {
            urls = classLoader.getResources(fileName);
        } else {
            urls = ClassLoader.getSystemResources(fileName);
        }
        if (urls != null) {
            //遍歷獲取到的URL,並調用loadResource去加載資源
            while (urls.hasMoreElements()) {
                java.net.URL resourceURL = urls.nextElement();
                loadResource(extensionClasses, classLoader, resourceURL);
            }
        }
    } catch (Throwable t) {
        logger.error("Exception occurred when loading extension class (interface: ...");
    }
}

/**
 * 在loadDirectory的基礎上,對每一個文件中的內容進行加載與解析
 */
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
    try {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {
                //按行讀取,每一行先去掉#號後面的內容(#號後面的內容爲註釋)
                final int ci = line.indexOf('#');
                if (ci >= 0) {
                    line = line.substring(0, ci);
                }
                line = line.trim();
                if (line.length() > 0) {
                    try {
                        String name = null;
                        //按「=」號截取name和擴展類的全限定類名,能夠看出name是能夠沒有的
                        int i = line.indexOf('=');
                        if (i > 0) {
                            name = line.substring(0, i).trim();
                            line = line.substring(i + 1).trim();
                        }
                        //若是line,即擴展類全限定類名不爲空,經過Class.forName獲取其Class,
                        //而後調用loadClass繼續加載該Class
                        if (line.length() > 0) {
                            loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
                        }
                    } catch (Throwable t) {
                        IllegalStateException e = new IllegalStateException("Failed to load ...");
                        exceptions.put(line, e);
                    }
                }
            }
        }
    } catch (Throwable t) {
        logger.error("Exception occurred when loading extension class (interface: ...");
    }
}

/**
 * 在loadResource的基礎上,對文件中的每行獲取到的Class進行分析,並進行緩存
 */
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
    //判斷配置的擴展類是否爲指定SPI接口的實現類
    if (!type.isAssignableFrom(clazz)) {
        throw new IllegalStateException("Error occurred when loading ... is not subtype of interface.");
    }
    if (clazz.isAnnotationPresent(Adaptive.class)) {
        //若擴展類有@Adaptive註解,將這個Class存入緩存「cachedAdaptiveClass」
        //注意:一個SPI接口配置文件中,只能配置一個有@Adaptive註解的擴展類
        cacheAdaptiveClass(clazz);
    } else if (isWrapperClass(clazz)) {
        //若擴展類具備clazz.getConstructor(type)這樣的構造器,則認爲其是一個具備包裝功能的擴展類,
        //並將其存入緩存「cachedWrapperClasses」,一個SPI接口能夠配置多個用於包裝的擴展類
        cacheWrapperClass(clazz);
    } else {
        //進入到這個分支,表示該Class只是一個普通的SPI接口擴展類。
        //判斷該擴展類是否具備無參構造器
        clazz.getConstructor();
        //若是該擴展類在SPI接口配置文件中未定義name,則判斷擴展類是否具備@Extension註解,
        //若是有,則以@Extension的value值做爲name;不然以擴展類的SimpleName做爲name,
        //而且,若是擴展類的SimpleName以SPI接口的SimpleName做爲後綴結尾,則name須要去掉該後綴
        if (StringUtils.isEmpty(name)) {
            name = findAnnotationName(clazz);
            if (name.length() == 0) {
                throw new IllegalStateException("No such extension name for the class ...");
            }
        }    
        //對name按照","進行分割
        String[] names = NAME_SEPARATOR.split(name);
        if (ArrayUtils.isNotEmpty(names)) {
            //names不爲空
            //若擴展類具備@Activate註解,則使用names數組的第一個元素做爲key,@Activate的Class爲value
            //將映射關係存入緩存「cachedActivates」
            cacheActivateClass(clazz, names[0]);
            for (String n : names) {
                //將該擴展類的name和Class存入緩存「cachedNames」,name保持爲names[0]
                cacheName(clazz, n);
                //將該擴展類的name和Class存入方法參數extensionClasses
                saveInExtensionClass(extensionClasses, clazz, name);
            }
        }
    }
}

  經過以上的源碼分析,咱們瞭解到getExtension方法獲取一個SPI接口的擴展類實例的流程分爲解析配置文件、加載並緩存擴展類、建立並加工擴展類實例幾個步驟。緩存

  在獲得一個最終可用的擴展類實例前,該實例會進行屬性注入與層層包裝,這些行爲在官網上被成爲擴展點特性,這裏咱們把它稱之爲「擴展類特性」。官方給出了4個特性,分別爲「擴展點自動包裝」、「擴展點自動裝配」、「擴展點自適應」以及「擴展點自動激活」。在下面的其他章節中,咱們將重點來研究這幾個擴展點特性。併發

 

4、擴展點自適應

  在分析與getExtension方法密切相關的擴展點自動裝配與擴展點自動包裝以前,咱們先來了解一下擴展點自適應這個特性。擴展點自適應是一個很是重要的特性,由於dubbo某些SPI接口的擴展並不想在框架啓動階段被加載,而是但願在拓展方法被調用時,根據運行時參數進行加載,這種動態決定調用哪一個擴展類實例的方式使得dubbo很是的靈活。此外,先了解這個特性,也有助於咱們更好的分析自動裝配與自動包裝的源碼。  

4.1.什麼是自適應擴展類

  自適應擴展類一樣是某個SPI接口的擴展類,該擴展類具有這樣的一些特徵:它沒有實際的業務邏輯,而是可以根據傳入的參數,動態的獲取對應的擴展類實例。能夠看一下官網給的例子:
/**
 * 一個模擬的SPI接口
 */
@SPI
public interface WheelMaker {
    Wheel makeWheel(URL url);
}

/**
 * SPI接口的自適應擴展類
 */
public class AdaptiveWheelMaker implements WheelMaker {
    //自適應擴展類該方法的邏輯爲經過URL中的參數,動態的獲取真正須要執行的SPI接口擴展類
    public Wheel makeWheel(URL url) {
        if (url == null) {
            throw new IllegalArgumentException("url == null");
        }      
        // 1.從URL中獲取WheelMaker名稱
        String wheelMakerName = url.getParameter("Wheel.maker");
        if (wheelMakerName == null) {
            throw new IllegalArgumentException("wheelMakerName == null");
        }
        // 2.經過SPI加載具體的WheelMaker
        WheelMaker wheelMaker = ExtensionLoader.getExtensionLoader(WheelMaker.class).getExtension(wheelMakerName);        
        // 3.調用目標方法
        return wheelMaker.makeWheel(URL url);
    }
}

/**
 * 一個模擬的SPI接口
 */
@SPI
public interface CarMaker {
    Car makeCar(URL url);
}

/**
 * 汽車製造者SPI接口的擴展類
 */
public class RaceCarMaker implements CarMaker {
    
    WheelMaker wheelMaker;
 
    //在injectExtension方法中會經過setter注入AdaptiveWheelMaker
    //目前咱們只知道自動裝配行爲的結果,緣由會在「擴展點自動裝配」小節分析
    public setWheelMaker(WheelMaker wheelMaker) {
        this.wheelMaker = wheelMaker;
    }
 
    //實現的方法
    public Car makeCar(URL url) {
        //實際調用AdaptiveWheelMaker的makeWheel方法,得到當前運行環境參數url下須要使用的Wheel擴展類
        Wheel wheel = wheelMaker.makeWheel(url);
        return new RaceCar(wheel, ...);
    }
}

  假設運行時傳入這樣一個url參數「dubbo://192.168.0.101:20880/XxxService?wheel.maker=MichelinWheelMaker」,那麼RaceCar最終的wheel爲擴展類「MichelinWheelMaker」製造出來的輪胎。app

4.2.獲取自適應擴展類實例

  讓咱們回到ExtensionLoader類,在該類中提供了getAdaptiveExtension方法用於獲取一個SPI接口的自適應擴展類的實例,這個方法的源碼分析以下:
/**************************************** 相關字段 ****************************************/

//緩存的自適應擴展類實例
private final Holder<Object> cachedAdaptiveInstance = new Holder<>();

//SPI接口的擴展類中具備@Adaptive註解的擴展類Class緩存
private volatile Class<?> cachedAdaptiveClass = null;

//建立自適應擴展類實例的異常信息
private volatile Throwable createAdaptiveInstanceError;

/**************************************** 相關方法 ****************************************/

/**
 * 獲取自適應擴展類的實例
 */
public T getAdaptiveExtension() {
    //從ExtensionLoader的緩存字段中獲取數據,cachedAdaptiveInstance爲一個Holder<Object>對象
    Object instance = cachedAdaptiveInstance.get();
    if (instance == null) {
        //須要判斷一下建立自適應擴展對象的Throwable緩存是否存在,若是存在,直接拋出
        if (createAdaptiveInstanceError == null) {
            //併發控制
            synchronized (cachedAdaptiveInstance) {
                instance = cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
                        //建立自適應擴展類實例
                        instance = createAdaptiveExtension();
                        //設置緩存
                        cachedAdaptiveInstance.set(instance);
                    } catch (Throwable t) {
                        createAdaptiveInstanceError = t;
                        throw new IllegalStateException("......");
                    }
                }
            }
        } else {
            throw new IllegalStateException("......");
        }
    }

    return (T) instance;
}

/**
 * 建立自適應擴展類實例
 */
private T createAdaptiveExtension() {
    try {
        //(1)調用getAdaptiveExtensionClass方法獲取自適應擴展類的Class;
        //(2)經過newInstance實例化一個自適應擴展類的對象;
        //(3)調用injectExtension方法向自適應拓展類的實例中注入依賴,參考「擴展點自動裝配」小節;
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        throw new IllegalStateException("......");
    }
}

/**
 * 獲取自適應擴展類的Class
 */
private Class<?> getAdaptiveExtensionClass() {
    //獲取該ExtensionLoader對應SPI接口配置的全部擴展類(參考「getExtensionClsses」小節) 
    getExtensionClasses();
    //檢查具備@Adaptive註解的擴展類緩存,若緩存不爲空,則直接返回緩存
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    //若是SPI接口配置的全部擴展類都沒有被@Adaptive標註,則建立自適應擴展類
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

/**
 * 建立自適應擴展類
 */
private Class<?> createAdaptiveExtensionClass() {
    //經過AdaptiveClassCodeGenerator的generate方法建立自適應擴展代碼
    //該方法會檢測SPI接口中是否有被@Adapative註解的方法,對於要生成自適應擴展類的SPI接口
    //必須至少包含一個被@Adaptive註解的方法,不然會拋出異常
    String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
    ClassLoader classLoader = findClassLoader();
    //獲取編譯器實現類,同樣是經過AdaptiveExtension進行獲取,獲取以後進行編譯
    org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    return compiler.compile(code, classLoader);
}

   經過getAdaptiveExtension方法的流程能夠發現,要想得到一個SPI接口的自適應擴展類實例,有2種方式:框架

  • 在SPI接口的配置文件中配置具備@Adaptive註解的擴展類,在執行解析SPI接口配置文件方法getExtensionClasses時,它會調用loadClass方法,該方法判斷擴展類是否具備@Adaptive註解,若是有,則將該類Class緩存到ExtensionLoader的字段「cachedAdaptiveClass」中,而後直接實例化該Class的實例並進行自動裝配;
  • 若是未配置@Adaptive修飾的擴展類,則Dubbo會使用字節碼技術建立一個自適應擴展類,前提是SPI接口上至少有一個被@Adaptive註解的方法;
  在上述兩種建立自適應擴展類實例的方法中,都與@Adaptive這個註解息息相關,該註解的代碼以下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
    String[] value() default {};
}

   能夠看到,@Adaptive註解既可使用在類上,又可使用在方法上。其包含一個字符串數組的屬性,在經過字節碼技術建立自適應擴展類時,該屬性參與到生成邏輯中,具體的建立邏輯咱們立刻經過下一節來了解。maven

4.3.關於建立自適應擴展類

  Dubbo經過字節碼技術建立一個自適應擴展類,第一步使用AdaptiveClassCodeGenerator類建立一個自適應擴展類的代碼字符串,第二步經過ExtensionLoader獲取字節碼編譯器Compiler並編譯加載第一步中的代碼字符串,拿到自適應擴展類的Class。下面咱們對每個步驟進行詳細的源碼分析。

4.3.1.代碼拼接

  回顧一下ExtensionLoader.createAdaptiveExtensionClass方法中調用邏輯
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();

  首先傳入SPI接口的Class與默認的擴展類名稱(即@SPI註解的value值)建立一個AdaptiveClassCodeGenerator實例,以後調用generate方法生成代碼,AdaptiveClassCodeGenerator相對應的源碼以下:ide

/**************************************** 相關字段 ****************************************/

//SPI接口類型
private final Class<?> type;
    
//SPI接口默認的擴展類名稱,即@SPI註解的value屬性值
private String defaultExtName;
 
/**************************************** 相關方法 ****************************************/

/**
 * 能夠看到,構造器並未作太多的事情,只是簡單的將傳入的參數爲成員變量賦值
 */
public AdaptiveClassCodeGenerator(Class<?> type, String defaultExtName) {
    this.type = type;
    this.defaultExtName = defaultExtName;
}

/**
 * 建立自適應擴展類的代碼串
 */
public String generate() {
    //遍歷SPI接口是否具備@Adaptive註解修飾的方法,若是沒有則拋出異常
    if (!hasAdaptiveMethod()) {
        throw new IllegalStateException("......");
    }
    //按照一個JAVA類的代碼組成順序,拼接代碼字符串
    StringBuilder code = new StringBuilder();
    //拼接包信息字符串: "package" + SPI接口所在的包
    code.append(generatePackageInfo()); 
    //拼接import字符串: "import" + ExtensionLoader的全限定名
    code.append(generateImports());
    //拼接類開頭字符串: "public class" + SPI接口簡單名 + "$Adaptive implements" + SPI接口全限定名 + "{"
    //注意:使用全限定名的緣由爲import代碼串中只導入ExtensionLoader的類,下同
    code.append(generateClassDeclaration());
    //拼接每個方法字符串,邏輯比較複雜,在generateMethod方法源碼中詳細說明
    Method[] methods = type.getMethods();
    for (Method method : methods) {
        code.append(generateMethod(method));
    }
    //拼接類結束字符串: "}"
    code.append("}");
    return code.toString();
}

/**
 * 檢測該SPI接口是否具備@Adaptive修飾的方法
 */
private boolean hasAdaptiveMethod() {
    return Arrays.stream(type.getMethods()).anyMatch(m -> m.isAnnotationPresent(Adaptive.class));
}

/**
 * 生成自適應擴展類的方法代碼串
 */
private String generateMethod(Method method) {
    //獲取方法返回值類型全限定名
    String methodReturnType = method.getReturnType().getCanonicalName(); 
    //獲取方法名
    String methodName = method.getName(); 
    //獲取方法內容,有無@Adaptive修飾的方法其方法內容不一樣,詳細見下generateMethodContent源碼
    String methodContent = generateMethodContent(method); 
    //獲取方法參數列表,格式爲"參數類型全限定名 arg0, 參數類型全限定名 arg1, ..."
    String methodArgs = generateMethodArguments(method);
    //獲取方法異常拋出,格式爲"throws 異常1全限定名, 異常2全限定名, ..."
    String methodThrows = generateMethodThrows(method);
    //獲取一個方法代碼串
    return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent);
}

/**
 * 生成自適應擴展類的方法體代碼串
 */
private String generateMethodContent(Method method) {
    Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
    StringBuilder code = new StringBuilder(512);
    //判斷當前要生成的方法是否有@Adaptive註解
    if (adaptiveAnnotation == null) {
        //對於沒有@Adaptive註解的方法,生成"throw new UnsupportedOperationException(...)"代碼
        return generateUnsupported(method);
    } else {
        //檢查方法參數列表中是否有類型爲"com.apache.dubbo.common.URL"的參數
        //簡單提一下,com.apache.dubbo.common.URL是dubbo框架中各組件的數據總線
        int urlTypeIndex = getUrlTypeIndex(method);           
        if (urlTypeIndex != -1) {
            //若是方法參數列表中有URL類型參數,則爲該參數生成判斷是否爲null以及賦值的代碼:
            //(generateUrlNullCheck方法比較簡單,不進行詳細的源碼分析,這裏只給出邏輯)
            //if (arg%d == null) throw new IllegalArgumentException("url == null");
            //com.apache.dubbo.common.URL url = arg%d;
            //d的值爲urlTypeIndex
            code.append(generateUrlNullCheck(urlTypeIndex));
        } else {
            //若是方法參數列表中沒有URL類型參數,則須要遍歷參數列表中每個參數的類型信息,
            //判斷哪個參數具備"public URL getXXX()"簽名形式的方法,若是有則中止遍歷且生成以下代碼:
            //(generateUrlAssignmentIndirectly方法比較簡單,不進行詳細的源碼分析,這裏只給出邏輯)
            //if (arg%d == null) 備註:此處的d爲具有"public URL getXXX()"方法的參數下標,從0開始
            //    throw new IllegalArgumentException("參數全限定名 + argument == null");
            //if (arg%d.getter方法名() == null) 
            //    throw new IllegalArgumentException(參數全限定名 + argument getUrl() == null);
            //com.apache.dubbo.common.URL url = arg%d.getter方法名();
            code.append(generateUrlAssignmentIndirectly(method));
        }
        
        //獲取該方法的@Adaptive註解的屬性值value(爲一個String數組),這裏列出處理邏輯
        //若是屬性值value爲非空數組,直接獲取數組內容便可;
        //若是value爲空數組,則需將SPI接口的類名按照駝峯命名法進行檢測,對每一個駝峯進行分割並插入"."號,
        //而後轉爲小寫,好比LoadBalance通過處理後,獲得load.balance
        String[] value = getMethodAdaptiveValue(adaptiveAnnotation);
        
        //判斷當前方法的參數列表中是否有類型爲org.apache.dubbo.rpc.Invocation的參數
        boolean hasInvocation = hasInvocationArgument(method);
        
        //爲當前方法參數列表中第一個類型爲org.apache.dubbo.rpc.Invocation的參數生成判null語句以及調用語句
        //if (arg%d == null) throw new IllegalArgumentException("invocation == null");
        //String methodName = arg%d.getMethodName();
        //%d是org.apache.dubbo.rpc.Invocation類型的參數在參數列表中的下標
        code.append(generateInvocationArgumentNullCheck(method));
        
        //使用前面獲取到的字符串數組value以及Invocation參數存在標識拼接獲取擴展類extName的代碼串,
        //這是自適應擴展類核心的代碼,由於自適應擴展類的目的就是要根據當前運行參數,
        //判斷應該獲取SPI接口的哪個擴展類,而ExtensionLoader.getExtension方法的參數就是這個extName
        //該方法邏輯比較複雜,判斷分支較多,詳細的分析在下面generateExtNameAssignment中
        code.append(generateExtNameAssignment(value, hasInvocation));
        
        //對上一步獲取的局部變量extName拼接其判null代碼
        //if(extName == null) throw new IllegalStateException("Failed to get extension name from...");
        code.append(generateExtNameNullCheck(value));
        
        //生成獲取extName對應擴展類實例的代碼串,以下:
        //%s extension = (%<s)ExtensionLoader.getExtensionLoader(%s.class).getExtension(extName);
        //其中%s爲成員變量type.getName,即SPI接口的Class的全限定名
        code.append(generateExtensionAssignment());

        //生成目標方法調用邏輯,被@Adaptive註解的方法,第一個任務是根據運行時參數獲取對應的擴展類實例,
        //第二個任務就是調用這個實例的同名方法。生成的方法調用代碼串格式爲:
        //(1)若是該方法無返回值"extension.方法名(arg0, arg2, ..., argN);"
        //(2)若是該方法有返回值"return extension.方法名(arg0, arg1, ..., argN);"
        //其中extension爲上一步生成的擴展類實例變量名,arg0、arg1就爲當前@Adaptive註解方法的參數名
        code.append(generateReturnAndInvocation(method));
    }

    return code.toString();
}

/**
 * 擴展名extName獲取代碼串
 */
private String generateExtNameAssignment(String[] value, boolean hasInvocation) {    
    String getNameCode = null;
    //反向遍歷value,目的是生成從URL中獲取拓展名的代碼,生成的代碼會賦值給getNameCode變量
    for (int i = value.length - 1; i >= 0; --i) {
        //當遍歷的元素是最後一個元素時
        if (i == value.length - 1) {
            //若默認擴展名不空(即@SPI註解的value值不空)
            if (null != defaultExtName) {
                //因爲protocol是URL的成員變量,可經過getProtocol方法獲取,其餘的則是從
                //URL的成員變量parameters(一個Map<String, String>)中獲取。
                //由於獲取方式不一樣,因此這裏要判斷value[i]是否爲protocol
                if (!"protocol".equals(value[i])) {
                    //須要判斷一下hasInvocation標識(即當前方法是否有Invocation參數)
                    if (hasInvocation) {     
                        //生成的代碼功能等價於下面的代碼:
                        //url.getMethodParameter(methodName, value[i], defaultExtName)             
                        //注意,methodName是generateInvocationArgumentNullCheck生成的局部變量,下同
                        getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
                    } else {
                        //生成的代碼功能等價於下面的代碼:
                        //url.getParameter(value[i], defaultExtName)
                        getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);
                    }                
                } else {
                    //生成的代碼功能等價於下面代碼:
                    //( url.getProtocol() == null ? defaultExtName : url.getProtocol() )
                    getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName);
                }
            //若默認擴展名爲空    
            } else {
                if (!"protocol".equals(value[i])) {
                    if (hasInvocation) {
                        //生成的代碼格式同上,即
                        //url.getMethodParameter(methodName, value[i], defaultExtName)  
                        getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
                    } else {
                        //生成的代碼功能等價於:url.getParameter(value[i])
                        getNameCode = String.format("url.getParameter(\"%s\")", value[i]);
                    }
                } else {
                    //生成的代碼功能等價於:url.getProtocol()
                    getNameCode = "url.getProtocol()";
                }
            }
        //當遍歷的元素不爲最後一個時    
        } else {
            if (!"protocol".equals(value[i])) {
                if (hasInvocation) {
                    //生成的代碼同上,即:
                    //url.getMethodParameter(methodName, value[i], defaultExtName)      
                    getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
                } else {
                    //在上一次獲取的getNameCode代碼結果基礎上,再次獲取getNameCode,即一層層的獲取值
                    //生成的代碼功能等價於下面的代碼:
                    //url.getParameter(value[i], getNameCode)
                    //以Transporter接口的connect方法爲例,最終生成的代碼以下:
                    //url.getParameter("client", url.getParameter("transporter", "netty"))
                    getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode);
                }
            } else {
                //在上一次獲取的getNameCode代碼結果基礎上,再次獲取getNameCode,即一層層的獲取值
                //生成的代碼功能等價於下面的代碼:
                //url.getProtocol() == null ? getNameCode : url.getProtocol()
                //以Protocol接口的connect方法爲例,最終生成的代碼以下:
                //url.getProtocol() == null ? "dubbo" : url.getProtocol()
                getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode);
            }
        }
    }
    
    //返回生成extName的代碼:"String extName = getNameCode;"
    return String.format(CODE_EXT_NAME_ASSIGNMENT, getNameCode);
}

 

   總結一下生成自適應擴展類代碼字符串的邏輯,首先Dubbo只會對具備@Adaptive註解的方法才生成詳細的代碼,而沒有@Adaptive註解的代碼直接生成一段拋出異常的代碼;其次生成的代碼邏輯會根據當前方法的參數以及@Adaptive、@SPI註解的value值等因素,獲取到擴展類的name,從而經過ExtensionLoader拿到相應的擴展類實例,並調用該實例的同名方法,能夠說是作了一個「動態分發」。

4.3.2.代碼編譯

  完成自適應擴展類的代碼串生成後,獲取一個字節碼編譯器對代碼進行編譯和加載:
 ClassLoader classLoader = findClassLoader();
    //獲取編譯器實現類,同樣是經過AdaptiveExtension進行獲取,獲取以後進行編譯
    //在這裏具體獲取Compiler的細節略去,最終獲取到JavassistCompiler類的實例進行編譯
    org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    return compiler.compile(code, classLoader);

  對於這段代碼,咱們能夠運用本小節學到知識,來分析一下Compiler接口的自適應擴展類是什麼。

  首先看一下Compiler這個SPI接口的配置文件:
adaptive=org.apache.dubbo.common.compiler.support.AdaptiveCompiler
jdk=org.apache.dubbo.common.compiler.support.JdkCompiler
javassist=org.apache.dubbo.common.compiler.support.JavassistCompiler

  一共配置了3個擴展類,根據name的名稱,能夠確定AdaptiveCompiler是具備@Adaptive註解的擴展類

@Adaptive
public class AdaptiveCompiler implements Compiler {

    private static volatile String DEFAULT_COMPILER;

    public static void setDefaultCompiler(String compiler) {
        DEFAULT_COMPILER = compiler;
    }

    @Override
    public Class<?> compile(String code, ClassLoader classLoader) {
        Compiler compiler;
        ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
        String name = DEFAULT_COMPILER; // copy reference
        if (name != null && name.length() > 0) {
            compiler = loader.getExtension(name);
        } else {
            compiler = loader.getDefaultExtension();
        }
        return compiler.compile(code, classLoader);
    }

}

  根據4.2小節中的代碼邏輯,在具備@Adaptive註解修飾的擴展類的前提下,自適應擴展類實例必定獲取到這個被修飾的類實例,所以「ExtensionLoader.getExtensionLoader(....Compiler.class).getAdaptiveExtension();」這段代碼獲取到了一個AdaptiveCompiler的實例。AdaptiveCompiler的compile方法很簡單,因爲成員變量「DEFAULT_COMPILER」始終爲null,其會調用ExtensionLoader的getDefaultExtension方法獲取默認的擴展類實例,即@SPI接口註解value值做爲name的實例,看看Compiler接口代碼:

@SPI("javassist")
public interface Compiler {
    Class<?> compile(String code, ClassLoader classLoader);
}

  其默認的擴展類的name爲「javassist」,即類「org.apache.dubbo.common.compiler.support.JavassistCompiler」,這個類的具體compile方法很少贅述,就是校驗代碼字符串格式的正確性,並進行加載。

 

5、擴展點自動裝配

  瞭解了擴展點自適應後,讓咱們回到擴展點自動裝配這個特性上來。
  在getExtension方法的源碼分析中,當擴展類實例經過反射被初始化後,便調用injectExtension方法爲其注入屬性,官方將獲取擴展類實例過程當中的這種行爲稱爲「擴展點自動裝配」。下面咱們來看一下自動裝配的源碼,看看Dubbo是如何進行自動裝配的:
/**
 * 自動裝配擴展類實例
 */
private T injectExtension(T instance) {
    try {
        //若是objectFactory不爲空,則進行自動裝配
        if (objectFactory != null) {
            //遍歷擴展類實例的全部方法
            for (Method method : instance.getClass().getMethods()) {
                 //檢測方法是否以set開頭、只有一個參數、訪問修飾符爲public
                if (isSetter(method)) {
                    //對於有@DisableInject的註解的setter方法,不須要注入
                    if (method.getAnnotation(DisableInject.class) != null) {
                        continue;
                    }
                    //獲取setter方法的參數類型
                    Class<?> pt = method.getParameterTypes()[0];
                    //判斷參數類型是不是原始類型(基本類型+String+基本類型的包裝類+Date)
                    if (ReflectUtils.isPrimitives(pt)) {
                        continue;
                    }
                    try {
                        //獲取要注入屬性名,好比setName方法中對應的屬性名爲name
                        String property = getSetterProperty(method);
                        //從objectFactory中根據屬性名與屬性的Class類型獲取依賴對象,
                        //獲取到的是一個SPI接口的自適應擴展類的對象或者Spring環境下的一個bean,
                        //objectFactory.getExtension方法的細節咱們在ExtensionFactory中將詳細討論
                        Object object = objectFactory.getExtension(pt, property);
                        if (object != null) {
                            //調用setter方法進行注入
                            method.invoke(instance, object);
                        }
                    } catch (Exception e) {
                        logger.error("Failed to inject via method ...");
                    }
                }
            }
        }
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    }
    return instance;
}

 

  對擴展類進行自動裝配時,裝配目標是名爲「setXXX」的方法映射出來的屬性,而且認爲該屬性的類型爲setXXX方法的參數類型,屬性名爲「XXX」部分第一個字母小寫以後加上其他字母。舉個例子,有方法「setMar(Car foo)」,則要注入的屬性類型爲Car,屬性名爲mar。獲取到裝配目標的類型信息和名稱信息後,調用objectFactory的getExtension方法獲取屬性的實例。      
  回憶一下,objectFactory這個對象,是getExtensionLoader方法中調用ExtensionLoader的私有構造器建立的,代碼以下: 
private ExtensionLoader(Class<?> type) {
    this.type = type;
    objectFactory = (type == ExtensionFactory.class ? null : 
                     ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

   這幾行簡單的代碼包含了不少信息:首先,ExtensionFactory自身也是一個SPI接口,其一樣可以經過ExtensionLoader進行初始化。其次,當獲取一個SPI接口的ExtensionLoader時,若是該SPI接口爲ExtensionFactory,則不會設置objectFactory字段值,不然會經過getAdaptiveExtension方法拿到一個ExtensionFactory的實例並將字段objectFactory的值設置爲它。

  在講解擴展點自適應的代碼編譯小節中,咱們分析了獲取Compiler自適應擴展類實例的流程。一樣的,在這裏咱們也能夠照葫蘆畫瓢的來分析獲取ExtensionFactory接口自適應擴展實例的流程。具體過程就不贅述了,最終獲取到的自適應擴展實例是一個AdaptiveExtensionFactory實例對象。下面咱們會花幾個小節介紹一個ExtensionFactory相關的家族成員。

5.1.ExtensionFactory

  ExtensionFactory是一個SPI接口,其源碼比較簡單,包含一個方法用於根據Class以及name獲取一個實例對象,方法名getExtension暗含了要獲取的是擴展類對象。咱們能夠大膽猜想,這個方法應該是根據SPI接口Class和其配置文件中擴展類的name獲取擴展類的實例。
@SPI
public interface ExtensionFactory {
    <T> T getExtension(Class<T> type, String name);
}

  ExtensionFactory這個接口的繼承樹以下圖所示:

 

  它有3個實現類,分別是AdaptiveExtensionFactory、SpiExtensionFactory、SpringExtensionFactory,相關的META-INFO配置文件內容以下:

#位於dubbo-common模塊下的META-INFO/dubbo/internal/com.apache.dubbo.common.ExtensionFactory文件
adaptive=org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=org.apache.dubbo.common.extension.factory.SpiExtensionFactory

#位於dubbo-spring模塊下的META-INF/dubbo/internal/com.apache.dubbo.common.ExtensionFactory文件
spring=org.apache.dubbo.config.spring.extension.SpringExtensionFactory

5.2.AdaptiveExtensionFactory

  其實從配置文件中AdaptiveExtensionFactory的name可知,其應該爲一個具備@Adaptive註解的擴展類做爲自適應擴展類。
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {

    //維護了一組ExtensionFctory接口擴展類的實例
    private final List<ExtensionFactory> factories;

    //構造方法,獲取全部ExtensionFactory配置的擴展類實例
    public AdaptiveExtensionFactory() {
        //加載ExtensionFactory對應的ExtensionLoader
        ExtensionLoader<ExtensionFactory> loader = 
            ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();、
        //getSupportedExtensions方法用於獲取ExtensionLoader的cachedClasses緩存的keySet
        //即SPI接口配置文件中name的Set集合    
        for (String name : loader.getSupportedExtensions()) {
            //根據name名字,調用getExtension方法獲取對應擴展類的實例並緩存
            list.add(loader.getExtension(name));
        }
        factories = Collections.unmodifiableList(list);
    }

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        for (ExtensionFactory factory : factories) {
            //調用每個ExtensionFactory實現類的getExtension方法,並返回第一個不爲null的對象
            T extension = factory.getExtension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

}

 

5.3.SpiExtensionFactory

  該Factory只支持具備@SPI註解的接口。
public class SpiExtensionFactory implements ExtensionFactory {

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
            //getSupportedExtensions方法用於獲取ExtensionLoader的cachedClasses緩存的keySet
            //即SPI接口配置文件中name的Set集合   
            if (!loader.getSupportedExtensions().isEmpty()) {
                return loader.getAdaptiveExtension();
            }
        }
        return null;
    }

}

 

5.4.SpringExtensionFactory

  顧名思義,這個Factory應用在Spring環境下,是dubbo與Spring結合使用的ExtensionFactory。與SpiExtensionFactory的行爲相反,它不支持@SPI註解的接口。
public class SpringExtensionFactory implements ExtensionFactory {
    private static final Logger logger = LoggerFactory.getLogger(SpringExtensionFactory.class);

    private static final Set<ApplicationContext> CONTEXTS = new ConcurrentHashSet<ApplicationContext>();
    private static final ApplicationListener SHUTDOWN_HOOK_LISTENER = new ShutdownHookListener();

    public static void addApplicationContext(ApplicationContext context) {
        CONTEXTS.add(context);
        if (context instanceof ConfigurableApplicationContext) {
            ((ConfigurableApplicationContext) context).registerShutdownHook();
            DubboShutdownHook.getDubboShutdownHook().unregister();
        }
        //先SpringContext註冊dubbo關閉鉤子
        BeanFactoryUtils.addApplicationListener(context, SHUTDOWN_HOOK_LISTENER);
    }

    //省略了一些CONTEXTS的CRUD方法...

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getExtension(Class<T> type, String name) {
        //不支持被@SPI註解標註的接口類型
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            return null;
        }
        //首先根據name從Spring上下文獲取bean,並驗證該bean與type是否一致
        for (ApplicationContext context : CONTEXTS) {
            if (context.containsBean(name)) {
                Object bean = context.getBean(name);
                if (type.isInstance(bean)) {
                    return (T) bean;
                }
            }
        }
        logger.warn("No spring extension (bean) named ...");
        //若是type的類型爲Object,直接返回null
        if (Object.class == type) {
            return null;
        }
        //嘗試根據type從Spring上下文獲取bean,若是type對應的bean並不是惟一,直接報錯
        for (ApplicationContext context : CONTEXTS) {
            try {
                return context.getBean(type);
            } catch (NoUniqueBeanDefinitionException multiBeanExe) {
                logger.warn("Find more than 1 spring extensions (beans) of type ...");
            } catch (NoSuchBeanDefinitionException noBeanExe) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Error when get spring extension(bean) for type: ...");
                }
            }
        }
        logger.warn("No spring extension (bean) named: ...");
        //未找到,返回null
        return null;
    }

    private static class ShutdownHookListener implements ApplicationListener {
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            if (event instanceof ContextClosedEvent) {
                DubboShutdownHook shutdownHook = DubboShutdownHook.getDubboShutdownHook();
                shutdownHook.doDestroy();
            }
        }
    }
}

   SpringExtensionFactory維護了Spring上下文集合「Set<ApplicationContext>」。在getExtension方法中,經過Spring上下文去獲取實例。而且,SpringExtensionFactory在設置SpringContext時,會向獲取到的Context註冊一個Spring容器關閉事件的監聽鉤子,用於關閉dubbo。  

 

6、擴展點自動包裝

  擴展點實例自動裝配以後,便開始進行自動包裝,包裝的過程爲遍歷緩存「cachedWrapperClasses」並將裝配好的擴展類實例做爲包裝擴展類的構造器參數,建立一個新的包裝類實例,而後對這個新的實例進行自動裝配。
  如何斷定一個擴展類是不是包裝類呢?在getExtension方法的源碼分析中已經有說起,就是某個SPI接口的擴展類具備一個特定特徵的構造函數,這個構造函數是單參數,而且參數類型是該擴展類實現的SPI接口類型。舉個官網給出的例子:
package com.alibaba.xxx;
 
import org.apache.dubbo.rpc.Protocol;
 
public class XxxProtocolWrapper implements Protocol {
    Protocol impl;
 
    //單參數構造函數,且參數類型爲其實現的SPI接口類型Protocol
    public XxxProtocolWrapper(Protocol protocol) { impl = protocol; }
 
    // 接口方法作一個操做後,再調用extension的方法
    public void refer() {
        //... 一些操做
        impl.refer();
        // ... 一些操做
    }
}

  自動包裝的機制可以近似的實現AOP的功能,經過Wrapper類能夠把全部擴展點公共邏輯移至Wrapper中,新加的Wrapper在全部的擴展點上添加了邏輯。

 

7、擴展點自動激活

  還未完成。
相關文章
相關標籤/搜索