中文字幕日韩精品一区二区免费_精品一区二区三区国产精品无卡在_国精品无码专区一区二区三区_国产αv三级中文在线

Dubbo的SPI機制介紹以及SPI加載class的方法

這篇文章主要講解了“Dubbo的SPI機制介紹以及SPI加載class的方法”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Dubbo的SPI機制介紹以及SPI加載class的方法”吧!

成都創(chuàng)新互聯(lián)公司是專業(yè)的祿勸網(wǎng)站建設(shè)公司,祿勸接單;提供網(wǎng)站設(shè)計制作、成都網(wǎng)站制作,網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進行祿勸網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團隊,希望更多企業(yè)前來合作!

1、Dubbo的SPI例子

@SPI
public interface Robot {
    void sayHello();
}

public class OptimusPrime implements Robot{
    @Override
    public void sayHello() {
        System.out.println("Hello, I am Optimus Prime.");
    }
}

public class Bumblebee implements Robot{
    @Override
    public void sayHello() {
        System.out.println("Hello, I am Bumblebee.");
    }
}
public class DubboSPITest {

    @Test
    public void sayHelloDubbo() throws Exception {
        ExtensionLoader<robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class);
        Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
        optimusPrime.sayHello();
        Robot bumblebee = extensionLoader.getExtension("bumblebee");
        bumblebee.sayHello();
    }
}
輸出:
Hello, I am Optimus Prime.
Hello, I am Bumblebee.

2、Dubbo的SPI源碼分析

ExtensionLoader<robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class);

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

    // 從緩存中獲取與拓展類對應(yīng)的ExtensionLoader
    ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    if (loader == null) {
        // 若緩存未命中,則創(chuàng)建一個新的實例,先簡單看下new ExtensionLoader<T>(type)
        EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
        loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    }
    return loader;
}

這里的objectFactory創(chuàng)建可以參考: Dubbo的SPI機制分析3-Dubbo的IOC依賴注入

 private ExtensionLoader(Class<?> type) {
    this.type = type;
    // 這里的type是Robot.class,所以會執(zhí)行后面的代碼,加載并創(chuàng)建SpiExtensionFactory和SpringExtensionFactory,
    objectFactory = (type == ExtensionFactory.class ? null : 
                             ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
public T getExtension(String name) {
    if ("true".equals(name)) {
        return getDefaultExtension();
    }
    // Holder也是用于持有對象的,用作緩存
    Holder<object> holder = cachedInstances.get(name);
    if (holder == null) {
        cachedInstances.putIfAbsent(name, new Holder<object>());
        holder = cachedInstances.get(name);
    }
    Object instance = holder.get();
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                // 創(chuàng)建拓展實例,下面分析createExtension(name)
                instance = createExtension(name);
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}

這里依賴注入可以參考: Dubbo的SPI機制分析3-Dubbo的IOC依賴注入

private T createExtension(String name) {
    // 從配置文件中加載所有的拓展類,可得到“配置項名稱”到“配置類”的映射關(guān)系表
    // 加載完后根據(jù)name獲取,得到形如com.alibaba.dubbo.demo.provider.spi.OptimusPrime
    // 待會重點分析getExtensionClasses(),刪去了一些非關(guān)鍵代碼
    Class<?> clazz = getExtensionClasses().get(name);
    try {
        // 也是嘗試先從緩存獲取,獲取不到通過反射創(chuàng)建一個并放到緩存中
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        // 依賴注入和cachedWrapperClasses,后面分析
        injectExtension(instance);
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
           for (Class<?> wrapperClass : wrapperClasses) {
              instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
           }
        }
        return instance;
    } 
}
 private Map<String, Class<?>> getExtensionClasses() {
    // 從緩存中獲取映射關(guān)系表
    Map<string, class<?>> classes = cachedClasses.get();
    // 雙重檢查
    if (classes == null) {
        synchronized (cachedClasses) {
            classes = cachedClasses.get();
            if (classes == null) {
                // 若緩存中沒有,去加載映射關(guān)系
                classes = loadExtensionClasses();
                cachedClasses.set(classes);
            }
        }
    }
    return classes;
}
private Map<string, class<?>> loadExtensionClasses() {
    // 獲取SPI注解,這里的type是在調(diào)用getExtensionLoader方法時傳入的
    final SPI defaultAnnotation = type.getAnnotation(SPI.class);
    if (defaultAnnotation != null) {
        String value = defaultAnnotation.value();
        if ((value = value.trim()).length() > 0) {
            String[] names = NAME_SEPARATOR.split(value);
            if (names.length > 1) {
                // 拋異常
            }
            // 獲取@SPI注解中的值,并緩存起來,可以關(guān)注下,后面會用到
            if (names.length == 1) cachedDefaultName = names[0];
        }
    }

    Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
    // 加載指定文件夾下的配置文件,META-INF/services/、META-INF/dubbo/、META-INF/dubbo/internal/
    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
    // 我是放在這個目錄下的
    loadDirectory(extensionClasses, DUBBO_DIRECTORY);
    loadDirectory(extensionClasses, SERVICES_DIRECTORY);
    return extensionClasses;
}
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) {
    // fileName是META-INF/dubbo/com.alibaba.dubbo.demo.provider.spi.Robot
    String fileName = dir + type.getName();
    try {
        Enumeration<java.net.url> urls;
        ClassLoader classLoader = findClassLoader();
        // 根據(jù)文件名加載所有同名文件
        if (classLoader != null) {
            urls = classLoader.getResources(fileName);
        } else {
            urls = ClassLoader.getSystemResources(fileName);
        }
        if (urls != null) {
            while (urls.hasMoreElements()) {
                // 一個resourceURL就是一個文件
                java.net.URL resourceURL = urls.nextElement();
                loadResource(extensionClasses, classLoader, resourceURL);
            }
        }
    } 
}
private void loadResource(Map<string, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
    try {
        BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
        try {
            String line;
            // 按行讀取配置內(nèi)容
            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);
                        }
                    }
                }
            }
        }
    } 
}

標(biāo)注1,這里的可以參考: Dubbo的SPI機制分析2-Adaptive詳解

標(biāo)注2,這里的可以參考: Dubbo的SPI機制分析4-Dubbo通過Wrapper實現(xiàn)AOP

標(biāo)注3,這里的cachedActivates可以參考: Dubbo的SPI機制分析5-Activate詳解

private void loadClass(Map<string, Class<?>> extensionClasses, java.net.URL resourceURL, 
                                                     Class<?> clazz, String name) throws NoSuchMethodException {
    // clazz必須是type類型的,否則拋異常
    if (!type.isAssignableFrom(clazz)) {
    }
    // 判斷clazz是否為標(biāo)注了@Adaptive注解,標(biāo)注1
    if (clazz.isAnnotationPresent(Adaptive.class)) {
        if (cachedAdaptiveClass == null) {
            cachedAdaptiveClass = clazz;
        } else if (!cachedAdaptiveClass.equals(clazz)) {
            // 拋異常,不能有多個標(biāo)注有@Adaptive注解的類
        }
    }
    // 判斷是否是Wrapper類型,標(biāo)注2
    else if (isWrapperClass(clazz)) {
        Set<Class<?>> wrappers = cachedWrapperClasses;
        if (wrappers == null) {
            cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
            wrappers = cachedWrapperClasses;
        }
        wrappers.add(clazz);
    }

    // 程序進入此分支,表明clazz是一個普通的拓展類,Robot就是一個普通的拓展類
    else {
        // 檢測clazz是否有默認(rèn)的構(gòu)造方法,如果沒有,則拋出異常
        clazz.getConstructor();
        if (name == null || name.length() == 0) {
            name = findAnnotationName(clazz);
            if (name.length() == 0) {
               // 拋異常
            }
        }

        String[] names = NAME_SEPARATOR.split(name);
        if (names != null && names.length > 0) {
            // 用于@Activate根據(jù)條件激活,標(biāo)注3
            Activate activate = clazz.getAnnotation(Activate.class);
            if (activate != null) {
                cachedActivates.put(names[0], activate);
            }
            for (String n : names) {
                if (!cachedNames.containsKey(clazz)) {
                    cachedNames.put(clazz, n);
                }
                Class<?> c = extensionClasses.get(n);
                if (c == null) {
                    // 存儲名稱到class的映射關(guān)系,這樣就解析好了一行
                    extensionClasses.put(n, clazz);
                } else if (c != clazz) {
                    // 拋異常
                }
            }
        }
    }
}

感謝各位的閱讀,以上就是“Dubbo的SPI機制介紹以及SPI加載class的方法”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對Dubbo的SPI機制介紹以及SPI加載class的方法這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!

文章名稱:Dubbo的SPI機制介紹以及SPI加載class的方法
轉(zhuǎn)載來于:http://www.rwnh.cn/article34/gshose.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供App開發(fā)、域名注冊、搜索引擎優(yōu)化、網(wǎng)站設(shè)計、標(biāo)簽優(yōu)化網(wǎng)站排名

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)公司
习水县| 山东省| 西林县| 泸西县| 孝义市| 莱州市| 保定市| 灌南县| 太湖县| 康乐县| 荣成市| 花莲县| 克拉玛依市| 镇原县| 斗六市| 天水市| 五莲县| 山西省| 西平县| 宕昌县| 吕梁市| 宽城| 正阳县| 天水市| 仲巴县| 自治县| 安平县| 梁平县| 道孚县| 文水县| 铜川市| 滦平县| 德化县| 民和| 秭归县| 瑞昌市| 精河县| 隆安县| 界首市| 泗洪县| 岑巩县|