DUBBO SPI實(shí)現(xiàn)核心-ExtensionLoader

概念

dubbo spi是dubbo對(duì)JDK spi的升級(jí),針對(duì)JDK spi的一些弱勢(shì)進(jìn)行了優(yōu)化,官網(wǎng)的介紹如下:

Dubbo 的擴(kuò)展點(diǎn)加載從 JDK 標(biāo)準(zhǔn)的 SPI (Service Provider Interface) 擴(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)加載失敗,連擴(kuò)展點(diǎn)的名稱(chēng)都拿不到了。比如:JDK 標(biāo)準(zhǔn)的 ScriptEngine,通過(guò) getName() 獲取腳本類(lèi)型的名稱(chēng),但如果 RubyScriptEngine 因?yàn)樗蕾?lài)的 jruby.jar 不存在,導(dǎo)致 RubyScriptEngine 類(lèi)加載失敗,這個(gè)失敗原因被吃掉了,和 ruby 對(duì)應(yīng)不起來(lái),當(dāng)用戶(hù)執(zhí)行 ruby 腳本>時(shí),會(huì)報(bào)不支持 ruby,而不是真正失敗的原因。
增加了對(duì)擴(kuò)展點(diǎn) IoC 和 AOP 的支持,一個(gè)擴(kuò)展點(diǎn)可以直接 setter 注入其它擴(kuò)展點(diǎn)。

附上鏈接:https://dubbo.incubator.apache.org/zh-cn/docs/dev/SPI.html

dubbo一方面用spi機(jī)制完成自己的默認(rèn)實(shí)現(xiàn),同時(shí)允許開(kāi)發(fā)者在不清楚dubbo代碼細(xì)節(jié)的同時(shí)根據(jù)自己需求對(duì)框架能力進(jìn)行拓展。模仿該設(shè)計(jì)方式,可以讓很多流程性業(yè)務(wù)編碼變得非常簡(jiǎn)潔。

環(huán)境搭建

從來(lái)不想在搭環(huán)境上浪費(fèi)太多時(shí)間,用最簡(jiǎn)單的方式,免得降低讀代碼的耐心,
不需要構(gòu)建spring應(yīng)用,普通工程即可,在pom中添加依賴(lài):

<dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>dubbo</artifactId>
      <version>2.6.6</version>
    </dependency>

    <dependency>
      <groupId>io.netty</groupId>
      <artifactId>netty-all</artifactId>
      <version>4.1.36.Final</version>
    </dependency>

    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>3.0.1</version>
    </dependency>

dubbo版本使用2.6.6,后面源碼介紹也是基于這個(gè)版本的,使用redis注冊(cè)中心。
接口以及其實(shí)現(xiàn)類(lèi):

public interface DemoService {
    String sayHello(String name);
}

public class DemoServiceImpl  implements DemoService{
    public String sayHello(String name) {
        return "Hello " + name;
    }
}

參考官方文檔,編寫(xiě)測(cè)試代碼:

// 服務(wù)實(shí)現(xiàn)
DemoService demoService = new DemoServiceImpl();

// 當(dāng)前應(yīng)用配置
ApplicationConfig application = new ApplicationConfig();
application.setName("dubbo-learn");

// 連接注冊(cè)中心配置
RegistryConfig registry = new RegistryConfig();
registry.setProtocol("redis");
registry.setAddress("127.0.0.1:6379");

// 服務(wù)提供者協(xié)議配置
ProtocolConfig protocol = new ProtocolConfig();
protocol.setName("dubbo");
protocol.setPort(12345);
protocol.setThreads(200);

// 服務(wù)提供者暴露服務(wù)配置
// 此實(shí)例很重,封裝了與注冊(cè)中心的連接,請(qǐng)自行緩存,否則可能造成內(nèi)存和連接泄漏
ServiceConfig<DemoService> service = new ServiceConfig<DemoService>();
service.setApplication(application);

// 多個(gè)注冊(cè)中心可以用setRegistries()
        service.setRegistry(registry);

        // 多個(gè)協(xié)議可以用setProtocols()
        service.setProtocol(protocol);
        service.setInterface(DemoService.class);
        service.setRef(demoService);
        service.setVersion("1.0.0");
        // service.setFilter("demoFilter");

        // 暴露及注冊(cè)服務(wù)
        service.export();

        // 此實(shí)例很重,封裝了與注冊(cè)中心的連接以及與提供者的連接,請(qǐng)自行緩存,否則可能造成內(nèi)存和連接泄漏
        ReferenceConfig<DemoService> reference = new ReferenceConfig<DemoService>();
        reference.setApplication(application);
        // 多個(gè)注冊(cè)中心可以用setRegistries()
        reference.setRegistry(registry);
        reference.setInterface(DemoService.class);
        reference.setVersion("1.0.0");

        // 注意:此代理對(duì)象內(nèi)部封裝了所有通訊細(xì)節(jié),對(duì)象較重,請(qǐng)緩存復(fù)用
        DemoService demoService1 = reference.get();
        System.out.println(demoService1.sayHello("jules"));

啟動(dòng)redis,就可以執(zhí)行測(cè)試代碼了,效果如圖


image.png

我們順便看下服務(wù)暴露以后,redis中保存的服務(wù)信息


image.png

可以看到注冊(cè)信息在redis是以哈希表的方式保存,哈希表的key即服務(wù)URL信息

源碼

初始化

在閱讀dubbo代碼時(shí),經(jīng)??吹饺缦聦?shí)現(xiàn):

private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

dubbo使用ExtensionLoader實(shí)現(xiàn)他的spi機(jī)制,以上代碼會(huì)生成一個(gè)Protocol的動(dòng)態(tài)代理實(shí)現(xiàn),由代理去執(zhí)行真實(shí)邏輯。

每個(gè)擴(kuò)展點(diǎn)對(duì)應(yīng)一個(gè)ExtensionLoader,getExtensionLoader(Protocol.class)實(shí)際為獲取Protocol.class的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!");
        }
        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;
    }

先從緩存中獲取,獲取不到則創(chuàng)建并放入緩存中,以供下次使用,ExtensionLoader只有一個(gè)構(gòu)造方法:

private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

獲取ExtensionLoader以后,執(zhí)行g(shù)etAdaptiveExtension()方法獲取動(dòng)態(tài)生成的Protocol,我們閱讀getAdaptiveExtension的代碼邏輯:

    public T getAdaptiveExtension() {
        // 從緩存中讀取
        Object instance = cachedAdaptiveInstance.get();
        if (instance == null) {
            // 檢查生成的時(shí)候是否出現(xiàn)異常
            if (createAdaptiveInstanceError == null) {
                synchronized (cachedAdaptiveInstance) {
                    instance = cachedAdaptiveInstance.get();
                    // 典型的單例生成方式,兩次驗(yàn)證
                    if (instance == null) {
                        try {
                            // 生成單例并且緩存起來(lái)
                            instance = createAdaptiveExtension();
                            cachedAdaptiveInstance.set(instance);
                        } catch (Throwable t) {
                            createAdaptiveInstanceError = t;
                            throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
                        }
                    }
                }
            } else {
                throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
            }
        }
        return (T) instance;
    }

以上代碼可知,核心邏輯在createAdaptiveExtension中,進(jìn)一步閱讀:

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

代理類(lèi)在getAdaptiveExtensionClass中生成,然后newInstance生成對(duì)象注入到injectExtension方法,于是先閱讀Class的創(chuàng)建過(guò)程,getAdaptiveExtensionClass的代碼為:

    private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

getExtensionClasses()篇幅比較長(zhǎng),主要是在第一次啟用的時(shí)候,加載所有的擴(kuò)展信息,getExtensionClasses調(diào)用loadExtensionClasses,我們直接看loadExtensionClasses的代碼:

    private Map<String, Class<?>> loadExtensionClasses() {
        // 讀取擴(kuò)展點(diǎn)上的value值,放入cachedDefaultName中
        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) {
                    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<?>>();
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
        loadDirectory(extensionClasses, DUBBO_DIRECTORY);
        loadDirectory(extensionClasses, SERVICES_DIRECTORY);
        return extensionClasses;
    }

dubbo的spi注解有一個(gè)String的value對(duì)象,以上代碼會(huì)讀取value值并存入cachedDefaultName中,可以看下Protocol的spi注解value給的值是dubbo,申明了默認(rèn)的實(shí)現(xiàn)


image.png

然后,連續(xù)調(diào)用了三個(gè)loadDirectory方法,三個(gè)DIRECTORY分別是:

  • META-INF/dubbo/internal/
  • META-INF/dubbo/
  • META-INF/services/
    其中META-INF/dubbo/internal/里面都是dubbo內(nèi)部的一些擴(kuò)展實(shí)現(xiàn),官網(wǎng)在介紹spi機(jī)制的時(shí)候約定的路徑是META-INF/dubbo/,理論上META-INF/services/也支持。
    打開(kāi)/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件,里面有dubbo實(shí)現(xiàn)的對(duì)擴(kuò)展點(diǎn)Protocol的實(shí)現(xiàn):
filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
rmi=com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol
hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
com.alibaba.dubbo.rpc.protocol.webservice.WebServiceProtocol
thrift=com.alibaba.dubbo.rpc.protocol.thrift.ThriftProtocol
memcached=com.alibaba.dubbo.rpc.protocol.memcached.MemcachedProtocol
redis=com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol
rest=com.alibaba.dubbo.rpc.protocol.rest.RestProtocol
registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
qos=com.alibaba.dubbo.qos.protocol.QosProtocolWrapper

Protocol的默認(rèn)實(shí)現(xiàn)類(lèi)是com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol。

loadDirectory調(diào)用loadResource,讀取/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件并且一行一行遍歷,獲取實(shí)現(xiàn)類(lèi)的class對(duì)象,然后在loadResource中會(huì)調(diào)用loadClass方法:

    private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
        // 處理Adaptive注解并且放到cachedAdaptiveClass中
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            if (cachedAdaptiveClass == null) {
                cachedAdaptiveClass = clazz;
            } else if (!cachedAdaptiveClass.equals(clazz)) {
                throw new IllegalStateException("More than 1 adaptive class found: "
                        + cachedAdaptiveClass.getClass().getName()
                        + ", " + clazz.getClass().getName());
            }
        // 包裝類(lèi),則放入wrappers中
        } else if (isWrapperClass(clazz)) {
            Set<Class<?>> wrappers = cachedWrapperClasses;
            if (wrappers == null) {
                cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
                wrappers = cachedWrapperClasses;
            }
            wrappers.add(clazz);
        } else {
            // 未帶注解的實(shí)現(xiàn)類(lèi),放入extensionClasses和cachedNames中
            clazz.getConstructor();
            String[] names = NAME_SEPARATOR.split(name);
            if (names != null && names.length > 0) {
                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) {
                        extensionClasses.put(n, clazz);
                    } else if (c != clazz) {
                        throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
                    }
                }
            }
        }
    }

這里引出了兩個(gè)個(gè)非常重要的注解:Adaptive和Activate,具體作用我們后面再說(shuō),閱讀上面代碼的第一個(gè)if可以確認(rèn),dubbo只允許一個(gè)實(shí)現(xiàn)類(lèi)上出現(xiàn)Adaptive注解,否則會(huì)出現(xiàn)IllegalStateException異常。
總結(jié)一下上面的代碼,遍歷/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件,獲取到內(nèi)部所有的class對(duì)象,會(huì)遇到四種情況:

類(lèi)別 處理 備注
實(shí)現(xiàn)類(lèi)并且?guī)Я薃daptive注解 放到cachedAdaptiveClass 只允許有一個(gè)帶有該注解
實(shí)現(xiàn)類(lèi)并且不帶Adaptive注解 放到cachedActivates和cachedNames -
實(shí)現(xiàn)類(lèi)并且?guī)ctivate注解和Adaptive注解 放到cachedActivates和cachedNames的同時(shí),放入cachedActivates -
包裝類(lèi) 放到cachedWrapperClasses -

我們現(xiàn)在回到getAdaptiveExtensionClass方法,避免去翻,在附上一次代碼:

    private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

通過(guò)閱讀,已經(jīng)了解getExtensionClasses方法主要是對(duì)擴(kuò)展文件的解析和實(shí)現(xiàn)類(lèi)的加載,其實(shí)也是為我們后續(xù)的調(diào)用做好準(zhǔn)備,加載需要做IO,好在每個(gè)ExtensionLoader只需要執(zhí)行一次具體的加載邏輯就行,初始化的時(shí)候已經(jīng)準(zhǔn)備就緒了。

接下來(lái)也是核心部分,也就是createAdaptiveExtensionClass方法,真正去創(chuàng)建Protocol代理實(shí)現(xiàn)類(lèi)的方法:

    private Class<?> createAdaptiveExtensionClass() {
        String code = createAdaptiveExtensionClassCode();
        ClassLoader classLoader = findClassLoader();
        com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader
         .getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class)
         .getAdaptiveExtension();
        return compiler.compile(code, classLoader);
    }

第一行調(diào)用createAdaptiveExtensionClassCode生成code,createAdaptiveExtensionClassCode方法篇幅很長(zhǎng),不再贅述了,功能是生成動(dòng)態(tài)類(lèi)的類(lèi)編碼,Protocol會(huì)生成如下代碼:

package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
    public void destroy() {
        throw new UnsupportedOperationException(
            "method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!"
        );
    }
    public int getDefaultPort() {
        throw new UnsupportedOperationException(
            "method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!"
        );
    }
    public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba
        .dubbo.rpc.RpcException {
            if (arg1 == null) throw new IllegalArgumentException("url == null");
            com.alibaba.dubbo.common.URL url = arg1;
            String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
            if (extName == null) throw new IllegalStateException(
                "Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() +
                ") use keys([protocol])");
            com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(
                com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
            return extension.refer(arg0, arg1);
        }
    public com.alibaba.dubbo.rpc.Exporter export (com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null) throw new IllegalArgumentException(
            "com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
        com.alibaba.dubbo.common.URL url = arg0.getUrl();
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null) throw new IllegalStateException(
            "Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() +
            ") use keys([protocol])");
        com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(
            com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.export(arg0);
    }
}

可以看到,代理類(lèi)實(shí)現(xiàn)擴(kuò)展點(diǎn)Protocol的所有實(shí)現(xiàn),其中對(duì)export和refer進(jìn)行了實(shí)現(xiàn),而getDefaultPort和destroy會(huì)直接異常,原因是Protocol的export和refer方法帶有注解@Adaptive,在createAdaptiveExtensionClassCode的邏輯中,只對(duì)有@Adaptive修飾的方法進(jìn)行實(shí)現(xiàn),@Adaptive可以注解在方法或者類(lèi)上,注解在方法上則會(huì)動(dòng)態(tài)生成實(shí)現(xiàn)方法,在該方法中會(huì)去查找真實(shí)需要執(zhí)行邏輯的實(shí)現(xiàn)類(lèi)(dubbo自帶的或者是開(kāi)發(fā)者擴(kuò)展的)。
在Protocol$Adaptive中通過(guò)

com.alibaba.dubbo.rpc.Protocol extension ExtensionLoader.getExtensionLoader(
                com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);

獲取到一個(gè)Protocol的實(shí)現(xiàn)類(lèi),然后調(diào)用這個(gè)實(shí)現(xiàn)類(lèi)對(duì)應(yīng)的方法,那么我們可以判斷,只有在發(fā)生真實(shí)調(diào)用的時(shí)候,我們才可以判斷具體是執(zhí)行了哪個(gè)實(shí)現(xiàn)類(lèi)的對(duì)應(yīng)方法。

最后,用對(duì)應(yīng)的classLoader實(shí)例化Protocol$Adaptive,初始化過(guò)程就完成了。

調(diào)用

接下來(lái)看下怎么使用這個(gè)代理類(lèi)吧,在Dubbo官網(wǎng)提供的分層模型中,Protocol位于遠(yuǎn)程調(diào)用層,主要封裝了RPC調(diào)用,其他幾個(gè)很重要的概念:Invoker,Invoker也在這一層,勸退重災(zāi)區(qū)。我們以protocol的refer進(jìn)行探索,refer方法會(huì)生成Invoker,基于不同的協(xié)議會(huì)生成不同的Invoker。
可以直接在com.alibaba.dubbo.config.ReferenceConfig#createProxy中進(jìn)行斷點(diǎn),Proxy初始化需要Invoker,Invoker的生成邏輯入口在createProxy中。
截取createProxy中的部分代碼

        if (isJvmRefer) {
            URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
            invoker = refprotocol.refer(interfaceClass, url);
            if (logger.isInfoEnabled()) {
                logger.info("Using injvm service " + interfaceClass.getName());
            }
        } else {
            if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
                String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
                if (us != null && us.length > 0) {
                    for (String u : us) {
                        URL url = URL.valueOf(u);
                        if (url.getPath() == null || url.getPath().length() == 0) {
                            url = url.setPath(interfaceName);
                        }
                        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                            urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                        } else {
                            urls.add(ClusterUtils.mergeUrl(url, map));
                        }
                    }
                }
            } else { // assemble URL from register center's configuration
                List<URL> us = loadRegistries(false);
                if (us != null && !us.isEmpty()) {
                    for (URL u : us) {
                        URL monitorUrl = loadMonitor(u);
                        if (monitorUrl != null) {
                            map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
                        }
                        urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                    }
                }
                if (urls.isEmpty()) {
                    throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
                }
            }

            if (urls.size() == 1) {
                invoker = refprotocol.refer(interfaceClass, urls.get(0));
            } else {
                List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
                URL registryURL = null;
                for (URL url : urls) {
                    invokers.add(refprotocol.refer(interfaceClass, url));
                    if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                        registryURL = url; // use last registry url
                    }
                }
                if (registryURL != null) { // registry url is available
                    // use AvailableCluster only when register's cluster is available
                    URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
                    invoker = cluster.join(new StaticDirectory(u, invokers));
                } else { // not a registry url
                    invoker = cluster.join(new StaticDirectory(invokers));
                }
            }
        }

這邊啰嗦下,目前的版本中,本地服務(wù)默認(rèn)就是本地調(diào)用了,也就是如果你的服務(wù)提供者和你的服務(wù)發(fā)起者都在本地,默認(rèn)是使用本地服務(wù),以上代碼中isJvmRefer會(huì)等于true,我在自己閱讀的時(shí)候,一般直接回在debug的時(shí)候去修改isJvmRefer的值以生成遠(yuǎn)程調(diào)用的Invoker,這次的關(guān)注點(diǎn)不在Invoker,我們關(guān)心的是refprotocol.refer(interfaceClass, url)在執(zhí)行的時(shí)候,是怎么找到對(duì)應(yīng)的實(shí)現(xiàn)類(lèi)的。
由之前的分析得知,這邊的refprotocol.refer其實(shí)是調(diào)用了Protocol$Adaptive.refer方法(我一直不知道怎么對(duì)動(dòng)態(tài)生成的類(lèi)進(jìn)行debug,是不是完全不行呢?),代碼是:

    public Invoker refer(Class arg0, URL arg1) throws RpcException {
            if (arg1 == null) throw new IllegalArgumentException("url == null");
                URL url = arg1;
            String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
            if (extName == null) throw new IllegalStateException(
                "Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() +
                ") use keys([protocol])");
            Protocol extension = (Protocol) ExtensionLoader.getExtensionLoader(
                Protocol.class).getExtension(extName);
            return extension.refer(arg0, arg1);
        }

這邊有兩行重要的代碼

String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);

根據(jù)URL信息(dubbo的協(xié)議都是存在url里面的)信息,獲取到Protocol,如果是null,就默認(rèn)使用dubbo,否則使用URL的配置,翻回去看下META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件的內(nèi)容,每個(gè)extName其實(shí)就是對(duì)應(yīng)的Protocol的實(shí)現(xiàn)。
我們回到ExtensionLoader,開(kāi)始閱讀getExtension的代碼

    public T getExtension(String name) {
        if (name == null || name.length() == 0)
            throw new IllegalArgumentException("Extension name == null");
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        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) {
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

cachedInstances用來(lái)緩存對(duì)應(yīng)的實(shí)現(xiàn),第一次進(jìn)來(lái)肯定是空的,dubbo只有在真正需要某個(gè)實(shí)現(xiàn)類(lèi)的時(shí)候才去實(shí)例化它,所以不需要擔(dān)心會(huì)有不使用但是很耗時(shí)的類(lèi)被實(shí)例化。然后執(zhí)行createExtension(name)實(shí)例化擴(kuò)展對(duì)象。

    private T createExtension(String name) {
        // 從緩存中拿到對(duì)應(yīng)的Class,在加載配置文件的時(shí)候已經(jīng)加入緩存
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            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;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                    type + ")  could not be instantiated: " + t.getMessage(), t);
        }
    }

實(shí)例化或者從緩存中拿到對(duì)應(yīng)的實(shí)例,injectExtension會(huì)進(jìn)行簡(jiǎn)單的依賴(lài)注入,然后返回實(shí)例,這時(shí)我們返回的參是真實(shí)要去執(zhí)行refer方法的Protocol實(shí)現(xiàn),本地服務(wù)就是InjvmProtocol。

至此,ExtensionLoader初始化 到執(zhí)行這個(gè)流程就結(jié)束了。


最后編輯于
?著作權(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)容