Dubbo之SPI實(shí)現(xiàn)原理詳解

開(kāi)篇

?SPI全稱為Service Provider Interface,是一種服務(wù)提供機(jī)制,比如在現(xiàn)實(shí)中我們經(jīng)常會(huì)有這種場(chǎng)景,就是對(duì)于一個(gè)規(guī)范定義方而言(可以理解為一個(gè)或多個(gè)接口),具體的服務(wù)實(shí)現(xiàn)方是不可知的(可以理解為對(duì)這些接口的實(shí)現(xiàn)類),那么在定義這些規(guī)范的時(shí)候,就需要規(guī)范定義方能夠通過(guò)一定的方式來(lái)獲取到這些服務(wù)提供方具體提供的是哪些服務(wù),而SPI就是進(jìn)行這種定義的。

JDK SPI例子

說(shuō)明:

  • 首先規(guī)范制定方會(huì)定義一個(gè)接口org.apache.jdk.spi.example.IHello 。

  • 其次在項(xiàng)目目錄下的META-INF/service名稱為org.apache.jdk.spi.example.IHello的文件,包含SPI實(shí)現(xiàn)接口全路徑。

  • 通過(guò)ServiceLoader加載訪問(wèn)調(diào)用即可。

  • 對(duì)于jdk的SPI,其主要存在兩個(gè)問(wèn)題,為每個(gè)接口提供的服務(wù)一般盡量只提供一個(gè),因?yàn)閖dk的SPI默認(rèn)會(huì)將所有目標(biāo)文件中定義的所有子類都讀取到返回使用;當(dāng)定義多個(gè)子類實(shí)現(xiàn)時(shí),無(wú)法動(dòng)態(tài)的根據(jù)配置來(lái)使用不同的配置。

---- 定義接口
package org.apache.jdk.spi.example;

public interface IHello {
    void sayHello();
}

---- 定義實(shí)現(xiàn)1
package org.apache.jdk.spi.example;

public class HelloImpl1 implements IHello {
    @Override
    public void sayHello() {
        System.out.println("我是Impl1");
    }
}

---- 定義實(shí)現(xiàn)2
package org.apache.jdk.spi.example;

public class HelloImpl2 implements IHello {
    @Override
    public void sayHello() {
        System.out.println("我是Impl2");
    }
}

---- META-INF/services目錄文件 org.apache.jdk.spi.example.IHello
org.apache.jdk.spi.example.HelloImpl1
org.apache.jdk.spi.example.HelloImpl2


---- 測(cè)試文件內(nèi)容
package org.apache.jdk.spi.example;

import java.util.Iterator;
import java.util.ServiceLoader;

public class ServiceLoaderDemo {
    public static void main(String[] args){
        ServiceLoader<IHello> s = ServiceLoader.load(IHello.class);
        Iterator<IHello> iHelloIterator = s.iterator();

        while (iHelloIterator.hasNext()) {
            IHello iHello = iHelloIterator.next();
            iHello.sayHello();
        }
    }
}
jdk spi目錄結(jié)構(gòu)


Dubbo SPI例子

  • 定義PlantsWater的接口并通過(guò)@SPI注解進(jìn)行注解,注解可選擇帶默認(rèn)值。

  • 將watering()方法使用@Adaptive注解進(jìn)行了標(biāo)注,表示該方法在自動(dòng)生成的子類中是需要?jiǎng)討B(tài)實(shí)現(xiàn)的方法。

  • 增加grant()方法是為了表明不帶@Adaptive在自動(dòng)生成的子類方法內(nèi)部會(huì)拋出異常。

  • 為PlantsWater增加兩個(gè)實(shí)現(xiàn),AppleWater和BananaWater,實(shí)際調(diào)用通過(guò)參數(shù)控制。

  • 在META-INF/dubbo下創(chuàng)建一個(gè)文件,該文件的名稱是目標(biāo)接口的全限定名,這里是org.apache.dubbo.spi.example.PlantsWater,在該文件中需要指定該接口所有可提供服務(wù)的子類。

  • 定義主函數(shù)ExtensionLoaderDemo模擬SPI調(diào)用的驗(yàn)證。

----定義基礎(chǔ)應(yīng)用類

public interface Fruit {}
public class Apple implements Fruit {}
public class Banana implements Fruit{}



----定義SPI類

@SPI("banana")
public interface PlantsWater {

    Fruit grant();

    @Adaptive
    String watering(URL url);
}


public class AppleWater implements PlantsWater {
    public Fruit grant() {
        return new Apple();
    }

    public String watering(URL url) {
        System.out.println("watering apple");
        return "watering finished";
    }
}


public class BananaWater implements PlantsWater {

    public Fruit grant() {
        return new Banana();
    }

    public String watering(URL url) {
        System.out.println("watering banana");
        return "watering success";
    }
}



----resources文件 org.apache.dubbo.spi.example.PlantsWater

apple=org.apache.dubbo.spi.example.AppleWater
banana=org.apache.dubbo.spi.example.BananaWater


------測(cè)試代碼內(nèi)容

public class ExtensionLoaderDemo {

    public static void main(String[] args) {
        // 首先創(chuàng)建一個(gè)模擬用的URL對(duì)象
        URL url = URL.valueOf("dubbo://192.168.0.101:20880?plants.water=apple");
        // 通過(guò)ExtensionLoader獲取一個(gè)PlantsWater對(duì)象,getAdaptiveExtension已經(jīng)加載了所有SPI類
        PlantsWater plantsWater = ExtensionLoader.getExtensionLoader(PlantsWater.class)
                .getAdaptiveExtension();
        // 使用該P(yáng)lantsWater調(diào)用其"自適應(yīng)標(biāo)注的"方法,獲取調(diào)用結(jié)果
        String result = plantsWater.watering(url);
        System.out.println(result);
    }
}


-----實(shí)際輸出內(nèi)容

十月 11, 2019 7:48:51 下午 org.apache.dubbo.common.logger.LoggerFactory info
信息: using logger: org.apache.dubbo.common.logger.jcl.JclLoggerAdapter
watering apple
watering finished

Process finished with exit code 0
dubbo spi目錄結(jié)構(gòu)


JDK 和 Dubbo SPI簡(jiǎn)單對(duì)比

Dubbo 的擴(kuò)展點(diǎn)加載是基于JDK 標(biāo)準(zhǔn)的 SPI 擴(kuò)展點(diǎn)發(fā)現(xiàn)機(jī)制增強(qiáng)而來(lái)的,Dubbo 改進(jìn)了 JDK 標(biāo)準(zhǔn)的 SPI 的以下問(wèn)題:

  • 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)加載失敗,就失敗了,給用戶沒(méi)有任何通知。比如:JDK 標(biāo)準(zhǔn)的ScriptEngine,如果Ruby ScriptEngine 因?yàn)樗蕾嚨?jruby.jar 不存在,導(dǎo)致 Ruby ScriptEngine 類加載失敗,這個(gè)失敗原因被吃掉了,當(dāng)用戶執(zhí)行 ruby 腳本時(shí),會(huì)報(bào)空指針異常,而不是報(bào)Ruby ScriptEngine不存在。

  • 增加了對(duì)擴(kuò)展點(diǎn) IoC 和 AOP 的支持,一個(gè)擴(kuò)展點(diǎn)可以直接 setter 注入其它擴(kuò)展點(diǎn)。


Dubbo SPI實(shí)現(xiàn)原理

dubbo對(duì)于SPI的實(shí)現(xiàn)主要是在ExtensionLoader這個(gè)類中,這個(gè)類主要有三個(gè)方法:

  • getExtension():主要用于獲取名稱為name的對(duì)應(yīng)的子類的對(duì)象,這里如果子類對(duì)象如果有AOP相關(guān)的配置,這里也會(huì)對(duì)其進(jìn)行封裝;
  • getAdaptiveExtension():使用定義的裝飾類來(lái)封裝目標(biāo)子類,具體使用哪個(gè)子類可以在定義的裝飾類中通過(guò)一定的條件進(jìn)行配置;
  • getExtensionLoader():加載當(dāng)前接口的子類并且實(shí)例化一個(gè)ExtensionLoader對(duì)象。
public T getExtension(String name);
public T getAdaptiveExtension();
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type);


getExtension()

  • getExtension()方法的主要作用是獲取name對(duì)應(yīng)的子類對(duì)象返回。
  • 其實(shí)現(xiàn)方式是首先讀取定義文件中的子類,然后根據(jù)不同的子類對(duì)象的功能的不同,比如使用@Adaptive修飾的裝飾類和用于AOP的Wrapper類,將其封裝到不同的緩存中。
  • 最后根據(jù)傳入的name獲取其對(duì)應(yīng)的子類對(duì)象,并且使用相應(yīng)的Wrapper類對(duì)其進(jìn)行封裝。

如下是getExtension()方法的源碼:

    public T getExtension(String name) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }

        // 如果名稱為true,則返回默認(rèn)的子類對(duì)象,這里默認(rèn)的子類對(duì)象的name定義在目標(biāo)接口的@SPI注解中
        if ("true".equals(name)) {
            return getDefaultExtension();
        }

        // 查看當(dāng)前是否已經(jīng)緩存有保存目標(biāo)對(duì)象的實(shí)例的Holder對(duì)象,緩存了則直接返回,
        // 沒(méi)緩存則創(chuàng)建一個(gè)并緩存起來(lái)
        final Holder<Object> holder = getOrCreateHolder(name);
        Object instance = holder.get();

        // 如果無(wú)法從Holder中獲取目標(biāo)對(duì)象的實(shí)例,則使用雙檢查法為目標(biāo)對(duì)象創(chuàng)建一個(gè)實(shí)例
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    // 創(chuàng)建name對(duì)應(yīng)的子類對(duì)象的實(shí)例
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }



    public T getDefaultExtension() {
        getExtensionClasses();
        if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) {
            return null;
        }
        // 通過(guò)cachedDefaultName去獲取對(duì)應(yīng)的子類實(shí)例
        return getExtension(cachedDefaultName);
    }


    private void cacheDefaultExtensionName() {
        // cachedDefaultName取自SPI的參數(shù)當(dāng)中
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation == null) {
            return;
        }

        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 on extension " + type.getName()
                        + ": " + Arrays.toString(names));
            }
            if (names.length == 1) {
                cachedDefaultName = names[0];
            }
        }
    }
  • 關(guān)于對(duì)于目標(biāo)對(duì)象的獲取,首先是從緩存里取,沒(méi)取到才會(huì)進(jìn)行創(chuàng)建。
  • 這里需要說(shuō)明的是,如果傳入的name為true,那么就會(huì)返回默認(rèn)的子類實(shí)例,而默認(rèn)的子類實(shí)例是通過(guò)其名稱進(jìn)行映射的,該名稱存儲(chǔ)在目標(biāo)接口的@SPI注解中。



createExtension()方法的源碼:

    private T createExtension(String name) {
        // 獲取當(dāng)前名稱對(duì)應(yīng)的子類類型,如果不存在,則拋出異常
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
            // 獲取當(dāng)前class對(duì)應(yīng)的實(shí)例,如果緩存中不存在,則實(shí)例化一個(gè)并緩存起來(lái)
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }

            // 為生成的實(shí)例通過(guò)其set方法注入對(duì)應(yīng)的實(shí)例,這里實(shí)例的獲取方式不僅可以通過(guò)SPI的方式
            // 也可以通過(guò)Spring的bean工廠獲取
            injectExtension(instance);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    // 實(shí)例化各個(gè)wrapper對(duì)象,并將目標(biāo)對(duì)象通過(guò)wrapper的構(gòu)造方法傳入,
                    // 另外還會(huì)通過(guò)wrapper對(duì)象的set方法對(duì)其依賴的屬性進(jìn)行注入
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                    type + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }

在createExtension()方法中,其主要做了三件事:

  • 加載定義文件中的各個(gè)子類,然后將目標(biāo)name對(duì)應(yīng)的子類返回后進(jìn)行實(shí)例化。
  • 通過(guò)目標(biāo)子類的set方法為其注入其所依賴的bean,這里既可以通過(guò)SPI,也可以通過(guò)Spring的BeanFactory獲取所依賴的bean,injectExtension(instance)。
  • 獲取定義文件中定義的wrapper對(duì)象,然后使用該wrapper對(duì)象封裝目標(biāo)對(duì)象,并且還會(huì)調(diào)用其set方法為wrapper對(duì)象注入其所依賴的屬性。

關(guān)于wrapper對(duì)象,這里需要說(shuō)明的是,其主要作用是為目標(biāo)對(duì)象實(shí)現(xiàn)AOP。wrapper對(duì)象有兩個(gè)特點(diǎn):

  • a. 與目標(biāo)對(duì)象實(shí)現(xiàn)了同一個(gè)接口;
  • b. 有一個(gè)以目標(biāo)接口為參數(shù)類型的構(gòu)造函數(shù)。這也就是上述createExtension()方法最后封裝wrapper對(duì)象時(shí)傳入的構(gòu)造函數(shù)實(shí)例始終可以為instance實(shí)例的原因。



getExtensionClasses()方法的源碼

    private Map<String, Class<?>> getExtensionClasses() {
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    // 加載定義文件,并且將定義的類按照功能緩存在不同的屬性中,即:
                    // a. 目標(biāo)class類型緩存在cachedClasses;
                    // b. wrapper的class類型緩存在cachedWrapperClasses;
                    // c. 用于裝飾的class類型緩存在cachedAdaptiveClass;
                    classes = loadExtensionClasses();
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }



    private Map<String, Class<?>> loadExtensionClasses() {
        // 獲取目標(biāo)接口上通過(guò)@SPI注解定義的默認(rèn)子類對(duì)應(yīng)的名稱,并將其緩存在cachedDefaultName中
        cacheDefaultExtensionName();


        // 分別在META-INF/dubbo/internal、META-INF/dubbo、META-INF/services目錄下
        // 獲取定義文件,并且讀取定義文件中的內(nèi)容,這里主要是通過(guò)META-INF/dubbo/internal
        // 獲取目標(biāo)定義文件
        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;
    }



    private void cacheDefaultExtensionName() {
        // 獲取目標(biāo)接口上通過(guò)@SPI注解定義的默認(rèn)子類對(duì)應(yīng)的名稱,并將其緩存在cachedDefaultName中
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation == null) {
            return;
        }

        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 on extension " + type.getName()
                        + ": " + Arrays.toString(names));
            }
            if (names.length == 1) {
                cachedDefaultName = names[0];
            }
        }
    }
  • loadExtensionClasses()主要是分別從三個(gè)目錄中讀取定義文件,讀取該文件,并且進(jìn)行緩存。



loadDirectory()方法的源碼:

    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) {
                while (urls.hasMoreElements()) {
                    // 對(duì)定義文件進(jìn)行遍歷,依次加載定義文件的內(nèi)容
                    java.net.URL resourceURL = urls.nextElement();
                    loadResource(extensionClasses, classLoader, resourceURL);
                }
            }
        } catch (Throwable t) {
            logger.error("Exception occurred when loading extension class (interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }



    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;
                            int i = line.indexOf('=');
                            if (i > 0) {
                                name = line.substring(0, i).trim();
                                line = line.substring(i + 1).trim();
                            }
                            if (line.length() > 0) {
                                loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
                            }
                        } catch (Throwable t) {
                            IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
                            exceptions.put(line, e);
                        }
                    }
                }
            }
        } catch (Throwable t) {
            logger.error("Exception occurred when loading extension class (interface: " +
                    type + ", class file: " + resourceURL + ") in " + resourceURL, t);
        }
    }
  • 這里主要是對(duì)每個(gè)目錄進(jìn)行加載,然后依次加載定義文件的內(nèi)容,而對(duì)定義文件內(nèi)容的處理主要是在loadResource()方法中,在對(duì)文件中每一行記錄進(jìn)行處理之后,其其最終是調(diào)用的loadClass()方法加載目標(biāo)class的。



loadClass()方法的源碼

    private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {

        // 如果加載得到的子類不是目標(biāo)接口的實(shí)現(xiàn)類,則拋出異常
        if (!type.isAssignableFrom(clazz)) {
            throw new IllegalStateException("Error occurred when loading extension class (interface: " +
                    type + ", class line: " + clazz.getName() + "), class "
                    + clazz.getName() + " is not subtype of interface.");
        }

        // 如果子類上標(biāo)注有@Adaptive注解,說(shuō)明其是一個(gè)裝飾類,則將其緩存在cachedAdaptiveClass中,
        // 需要注意的是,一個(gè)接口只能為其定義一個(gè)裝飾類
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            cacheAdaptiveClass(clazz);

        // 這里判斷子類是否是一個(gè)wrapper類,判斷方式就是檢查其是否有只含一個(gè)目標(biāo)接口類型參數(shù)的構(gòu)造函數(shù),
        // 有則說(shuō)明其是一個(gè)AOP的wrapper類
        } else if (isWrapperClass(clazz)) {
            cacheWrapperClass(clazz);
        } else {
            // 走到這里說(shuō)明當(dāng)前子類不是一個(gè)功能型的類,而是最終實(shí)現(xiàn)具體目標(biāo)的子類
            clazz.getConstructor();
            if (StringUtils.isEmpty(name)) {
                name = findAnnotationName(clazz);
                if (name.length() == 0) {
                    throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
                }
            }

            String[] names = NAME_SEPARATOR.split(name);
            if (ArrayUtils.isNotEmpty(names)) {
                // 緩存ActivateClass類
                cacheActivateClass(clazz, names[0]);

                // 將目標(biāo)子類緩存到extensionClasses中
                for (String n : names) {
                    cacheName(clazz, n);
                    saveInExtensionClass(extensionClasses, clazz, n);
                }
            }
        }
    }


    private void cacheActivateClass(Class<?> clazz, String name) {
        // 獲取子類上的@Activate注解,該注解的主要作用是對(duì)子類進(jìn)行分組的,
        // 對(duì)于分組之后的子類,可以通過(guò)getActivateExtension()來(lái)獲取
        Activate activate = clazz.getAnnotation(Activate.class);
        if (activate != null) {
            cachedActivates.put(name, activate);
        } else {
            // 兼容alibaba版本的注解
            com.alibaba.dubbo.common.extension.Activate oldActivate = clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.class);
            if (oldActivate != null) {
                cachedActivates.put(name, oldActivate);
            }
        }
    }


    private void saveInExtensionClass(Map<String, Class<?>> extensionClasses, Class<?> clazz, String name) {
        // 將目標(biāo)子類緩存到extensionClasses中
        Class<?> c = extensionClasses.get(name);
        if (c == null) {
            extensionClasses.put(name, clazz);
        } else if (c != clazz) {
            String duplicateMsg = "Duplicate extension " + type.getName() + " name " + name + " on " + c.getName() + " and " + clazz.getName();
            logger.error(duplicateMsg);
            throw new IllegalStateException(duplicateMsg);
        }
    }

loadClass()方法主要作用是對(duì)子類進(jìn)行劃分,這里主要?jiǎng)澐殖闪巳糠郑?/p>

  • 使用@Adaptive注解標(biāo)注的裝飾類;
  • 包含有目標(biāo)接口類型參數(shù)構(gòu)造函數(shù)的wrapper類
  • 目標(biāo)處理具體業(yè)務(wù)的子類。

總結(jié)而言,getExtension()方法主要是獲取指定名稱對(duì)應(yīng)的子類。在獲取過(guò)程中,首先會(huì)從緩存中獲取是否已經(jīng)加載過(guò)該子類,如果沒(méi)加載過(guò)則通過(guò)定義文件加載,并且使用獲取到的wrapper對(duì)象封裝目標(biāo)對(duì)象返回。


getAdaptiveExtension()

  • ExtensionLoader在加載了定義文件之后會(huì)對(duì)子類進(jìn)行一個(gè)劃分,使用@Adaptive進(jìn)行標(biāo)注的子類和使用@Adaptive標(biāo)注子類方法。

  • 使用@Adaptive進(jìn)行標(biāo)注的子類該子類的作用主要是用于對(duì)目標(biāo)類進(jìn)行裝飾的,從而實(shí)現(xiàn)一定的目的。

  • 使用@Adaptive進(jìn)行標(biāo)注的方法,其使用的方式主要是在目標(biāo)接口的某個(gè)方法上進(jìn)行標(biāo)注,這個(gè)時(shí)候,dubbo就會(huì)通過(guò)javassist字節(jié)碼生成工具來(lái)動(dòng)態(tài)的生成目標(biāo)接口的子類對(duì)象,該子類會(huì)對(duì)該接口中標(biāo)注了@Adaptive注解的方法進(jìn)行重寫(xiě),而其余的方法則默認(rèn)拋出異常,通過(guò)這種方式可以達(dá)到對(duì)特定的方法進(jìn)行修飾的目的。



getAdaptiveExtension()方法源碼

    public T getAdaptiveExtension() {
        // 從緩存中獲取裝飾類的實(shí)例,存在則直接返回,不存在則創(chuàng)建一個(gè)緩存起來(lái),然后返回
        Object instance = cachedAdaptiveInstance.get();
        if (instance == null) {
            if (createAdaptiveInstanceError != null) {
                throw new IllegalStateException("Failed to create adaptive instance: " +
                        createAdaptiveInstanceError.toString(),
                        createAdaptiveInstanceError);
            }

            synchronized (cachedAdaptiveInstance) {
                instance = cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
                       // 創(chuàng)建一個(gè)裝飾類的實(shí)例
                        instance = createAdaptiveExtension();
                        cachedAdaptiveInstance.set(instance);
                    } catch (Throwable t) {
                        createAdaptiveInstanceError = t;
                        throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
                    }
                }
            }
        }

        return (T) instance;
    }
  • 從緩存中獲取目標(biāo)類的實(shí)例,不存在則創(chuàng)建一個(gè)該實(shí)例。



    createAdaptiveExtension()方法源碼

    private T createAdaptiveExtension() {
        try {
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }


    private Class<?> getAdaptiveExtensionClass() {
        // 獲取目標(biāo)extensionClasses,如果無(wú)法獲取到,則在定義文件中進(jìn)行加載
        getExtensionClasses();

       // 如果目標(biāo)類型有使用@Adaptive標(biāo)注的子類型,則直接使用該子類作為裝飾類
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }

        // 如果目標(biāo)類型沒(méi)有使用@Adaptive標(biāo)注的子類型,則嘗試在目標(biāo)接口中查找是否有使用@Adaptive標(biāo)注的
        // 方法,如果有,則為該方法動(dòng)態(tài)生成子類裝飾代碼
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

    private Class<?> createAdaptiveExtensionClass() {
        // 創(chuàng)建子類代碼的字符串對(duì)象
        String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();

        // 獲取當(dāng)前dubbo SPI中定義的Compiler接口的子類對(duì)象,默認(rèn)是使用javassist,
        // 然后通過(guò)該對(duì)象來(lái)編譯生成的code,從而動(dòng)態(tài)生成一個(gè)class對(duì)象
        ClassLoader classLoader = findClassLoader();
        org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        return compiler.compile(code, classLoader);
    }

  • createAdaptiveExtension()首先委托給getAdaptiveExtensionClass()方法獲取一個(gè)裝飾類實(shí)例,然后通過(guò)injectExtension()方法調(diào)用該實(shí)例的set方法來(lái)注入其所依賴的屬性值。

  • 對(duì)于沒(méi)有使用@Adaptive標(biāo)注的子類時(shí),才會(huì)使用Javassist來(lái)為目標(biāo)接口生成其子類的裝飾方法。

  • 對(duì)于使用@Adaptive標(biāo)注的子類時(shí),直接返回子類。

  • createAdaptiveExtensionClass()動(dòng)態(tài)生成目標(biāo)接口的子類字符串,然后通過(guò)javassit來(lái)編譯該子類字符串,從而動(dòng)態(tài)生成目標(biāo)class。


getExtensionLoader()

    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 an interface!");
        }
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type (" + type +
                    ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
        }

        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;
    }
  • 對(duì)于ExtensionLoader的獲取,其實(shí)現(xiàn)過(guò)程比較簡(jiǎn)單,主要是從緩存中獲取,如果緩存不存在,則實(shí)例化一個(gè)并且緩存起來(lái)。


ExtensionLoader加載流程圖

Dubbo SPI

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容