結合Dubbo源碼分析Spi

如前所說,Dubbo SPI的目的是獲取一個指定實現類的對象。那麼Dubbo是經過什麼方式獲取的呢?實際上是調用ExtensionLoader.getExtension(String name)實現。java

具體實現途徑有三種:
①getExtensionLoader(Class<T> type) 爲type接口new一個ExtensionLoader,而後緩存起來。
②getAdaptiveExtension() 獲取一個擴展裝飾類的對象,這個類有一個規則,若是它沒有@Adaptive註解,就動態建立一個裝飾類,例如Protocol$Adaptive對象。
③getExtension(String name) 獲取一個指定對象。git

(1)分析ExtensionLoader.getExtensionLoader(Class<T> type)github

Dubbo的第一行代碼在哪裏?web

idea導入Dubbo源碼,在子模塊dubbo-demo-provider/src/test下有DemoProvider.javaredis

package com.alibaba.dubbo.demo.provider;

public class DemoProvider {

    public static void main(String[] args) {
        com.alibaba.dubbo.container.Main.main(args);
    }
}

這裏即是代碼的入口。
這裏調到com.alibaba.dubbo.container.Main.javaspring

package com.alibaba.dubbo.container;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.common.utils.ConfigUtils;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * Main. (API, Static, ThreadSafe)
 *
 * @author william.liangf
 */
public class Main {

    public static final String CONTAINER_KEY = "dubbo.container";

    public static final String SHUTDOWN_HOOK_KEY = "dubbo.shutdown.hook";

    private static final Logger logger = LoggerFactory.getLogger(Main.class);

    private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);

    private static volatile boolean running = true;

    public static void main(String[] args) {
        try {
            if (args == null || args.length == 0) {
                String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName());
                args = Constants.COMMA_SPLIT_PATTERN.split(config);
            }

            final List<Container> containers = new ArrayList<Container>();
            for (int i = 0; i < args.length; i++) {
                containers.add(loader.getExtension(args[i]));
            }
            logger.info("Use container type(" + Arrays.toString(args) + ") to run dubbo serivce.");

            if ("true".equals(System.getProperty(SHUTDOWN_HOOK_KEY))) {
                Runtime.getRuntime().addShutdownHook(new Thread() {
                    public void run() {
                        for (Container container : containers) {
                            try {
                                container.stop();
                                logger.info("Dubbo " + container.getClass().getSimpleName() + " stopped!");
                            } catch (Throwable t) {
                                logger.error(t.getMessage(), t);
                            }
                            synchronized (Main.class) {
                                running = false;
                                Main.class.notify();
                            }
                        }
                    }
                });
            }

            for (Container container : containers) {
                container.start();
                logger.info("Dubbo " + container.getClass().getSimpleName() + " started!");
            }
            System.out.println(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]").format(new Date()) + " Dubbo service server started!");
        } catch (RuntimeException e) {
            e.printStackTrace();
            logger.error(e.getMessage(), e);
            System.exit(1);
        }
        synchronized (Main.class) {
            while (running) {
                try {
                    Main.class.wait();
                } catch (Throwable e) {
                }
            }
        }
    }

}

能夠看到,Main類中定義了一系列的靜態成員變量,其中:設計模式

private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);

在Main類初始化階段調用了上述第①條方式爲Container建立擴展點。
經過斷點跟進getExtensionLoader方法,會進行new ExtensionLoader<T>(type)構造:緩存

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

能夠看到,這裏會進一步調用getExtensionLoader方法,只是此次傳入的是ExtensionFactory.class。經過上面的代碼知道,等價於以下:app

this.type = type;
objectFactory = null;

執行以上代碼完成了2個屬性的初始化:
1.每一個ExtensionLoader都包含了2個值: type 和 objectFactory
Class<?> type;//構造器初始化時要獲得的接口名
ExtensionFactory objectFactory//構造器初始化時設置爲AdaptiveExtensionFactory,Dubbo內部默認的實現是SpiExtensionFactory和SpringExtensionFactory。
2.new 一個ExtensionLoader 存儲在ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS裏。框架

關於objectFactory
1.objectFactory就是ExtensionFactory,它也是經過ExtensionLoader.getExtensionLoader(ExtensionFactory.class)來實現的,可是它的objectFactory=null
2.objectFactory做用,它就是爲dubbo的IOC提供全部對象。

(2)分析getAdaptiveExtension()
爲何要設計Adaptive?
Adaptive註解在類和方法上有什麼區別?
①註解在類上,表明人工實現編碼,即實現了一個裝飾類,如ExtensionFactory。
②註解在方法上,表明自動生成和編譯一個動態的adaptive類,如Protocol$Adaptive。

接下來從子模塊dubbo-config-spring下的schema包的DubboNamespaceHandler開始分析:

package com.alibaba.dubbo.config.spring.schema;

import com.alibaba.dubbo.common.Version;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ConsumerConfig;
import com.alibaba.dubbo.config.ModuleConfig;
import com.alibaba.dubbo.config.MonitorConfig;
import com.alibaba.dubbo.config.ProtocolConfig;
import com.alibaba.dubbo.config.ProviderConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.config.spring.AnnotationBean;
import com.alibaba.dubbo.config.spring.ReferenceBean;
import com.alibaba.dubbo.config.spring.ServiceBean;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

/**
 * DubboNamespaceHandler
 *
 * @author william.liangf
 * @export
 */
public class DubboNamespaceHandler extends NamespaceHandlerSupport {

    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }

    public void init() {
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
    }
}

先來看

registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));

這裏ServiceBean繼承自ServiceConfig類。

public class ServiceConfig<T> extends AbstractServiceConfig {

    private static final long serialVersionUID = 3033787999037024738L;

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

    private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
....
}

在這裏經過getAdaptiveExtension()獲取protocol。

-->getAdaptiveExtension()//爲cachedAdaptiveInstance賦值
  -->createAdaptiveExtension()
    -->getAdaptiveExtensionClass()//該方法看出,若是是預約義的類就直接返回,否則動態生成適配類
      -->getExtensionClasses()//爲cachedClasses 賦值
        -->loadExtensionClasses()
          -->loadFile(..)
      -->createAdaptiveExtensionClass()//自動生成和編譯一個動態的adpative類,這個類是一個代理類
        -->ExtensionLoader.getExtensionLoader
                  (com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension()
        -->compiler.compile(code, classLoader)
    -->injectExtension()//做用:進入IOC的反轉控制模式,實現了動態注入

loadFile(..)方法的做用:把SPI配置文件(如META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol)的內容,存儲在緩存變量裏。使用了四個緩存變量。
①緩存包含Adaptive註解的類
cachedAdaptiveClass 若是這個Class含有adaptive註解就賦值進去,如ExtensionFactory有,而Protocol沒有。
②緩存無Adaptive註解的封裝類
cachedWrapperClasses 只有當該class無adaptive註解,而且構造方法參數爲目標接口(type,如Protocol)類型,如Protocol裏的SPI就只有ProtocolFilterWrapper和ProtocolListenerWrapper能命中,以下例:

public class ProtocolFilterWrapper implements Protocol {

    private final Protocol protocol;

    public ProtocolFilterWrapper(Protocol protocol) {
        if (protocol == null) {
            throw new IllegalArgumentException("protocol == null");
        }
        this.protocol = protocol;
    }
  。。。
}

③cachedActivates 剩下的包含Activate註解的類
④cachedName 剩下的類存儲在該map中
在loadExtensionClasses()方法中,有三處loadFile()加載SPI文件:

private Map<String, Class<?>> loadExtensionClasses() {
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            String value = defaultAnnotation.value();
            if (value != null && (value = value.trim()).length() > 0) {
                String[] names = NAME_SEPARATOR.split(value);
                if (names.length > 1) {
                    throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
                            + ": " + Arrays.toString(names));
                }
                if (names.length == 1) cachedDefaultName = names[0];
            }
        }

        Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
        loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
        loadFile(extensionClasses, DUBBO_DIRECTORY);
        loadFile(extensionClasses, SERVICES_DIRECTORY);
        return extensionClasses;
    }

這裏的三處loadFile()實際上起到真正做用的是第一個:路徑爲META-INF/dubbo/internal/,這個打開dubbo.jar便可看到,這裏仍然看com.alibaba.dubbo.rpc.Protocol這個SPI文件:

registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol
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

上面執行compile時,框架會自動生成以下Protocol$Adpative類代碼:

package com.alibaba.dubbo.rpc;

import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class Protocol$Adpative 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.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);//本身執行本身,說明當前類是一個代理類
    }

    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);//本身執行本身,說明當前類是一個代理類
    }
}

其實就是根據以下模板生成的:

package <擴展點接口所在包>;
 
public class <擴展點接口名>$Adpative implements <擴展點接口> {
    public <有@Adaptive註解的接口方法>(<方法參數>) {
        if(是否有URL類型方法參數?) 使用該URL參數
        else if(是否有方法類型上有URL屬性) 使用該URL屬性
        # <else 在加載擴展點生成自適應擴展點類時拋異常,即加載擴展點失敗!>
         
        if(獲取的URL == null) {
            throw new IllegalArgumentException("url == null");
        }
 
              根據@Adaptive註解上聲明的Key的順序,從URL獲致Value,做爲實際擴展點名。
               如URL沒有Value,則使用缺省擴展點實現。如沒有擴展點, throw new IllegalStateException("Fail to get extension");
 
               在擴展點實現調用該方法,並返回結果。
    }
 
    public <有@Adaptive註解的接口方法>(<方法參數>) {
        throw new UnsupportedOperationException("is not adaptive method!");
    }
}

總結起來,Dubbo的全部對象都是經過ExtensionLoader獲取的,SPI是內核。

(3)分析getExtension(String name)

爲了進一步分析代理類的擴展類對象生成過程,將Protocol$Adpative類手動建立到dubbo源碼子模塊dubbo-demo下的dubbo-demo-provider中,test目錄下新建包com.alibaba.dubbo.rpc。而後將上述代碼拷貝其中。
而後在getExtension(extName)這裏設置斷點:

經過斷點跟蹤,調用鏈以下:

-->getExtension(String name) //指定對象緩存在cachedInstances;get出來的對象多是wrapper對象,例如protocol就是ProtocolFilterWrapper和ProtocolListenerWrapper其中一個。
  -->createExtension(String name)
    -->getExtensionClasses() //前面已經分析過,就是使用loadFile讀取文件並緩存
    -->injectExtension(T instance)//dubbo的IOC反轉控制,就是從spi和spring裏面提取對象賦值。
      -->objectFactory.getExtension(pt, property)//經過ExtensionFactory獲取extension,有兩種
        -->①SpiExtensionFactory.getExtension(type, name)
          -->ExtensionLoader.getExtensionLoader(type)
          -->loader.getAdaptiveExtension()
        -->②SpringExtensionFactory.getExtension(type, name)
          -->context.getBean(name)
    -->injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance))//AOP的簡單設計,這個地方若是前面的wrapperClasses緩存不空,那麼就會執行這句代碼,如Protocol中只有Filter和Listener,經過使用ProtocolFilterWrapper或ProtocolListenerWrapper的構造方法反射而後注入

經過上述分析,總結起來SPI getExtension()的執行流程及設計模式以下:

相關文章
相關標籤/搜索