1、dubbo源碼從入門到放棄-SPI

1、dubbo源碼從入門到放棄-SPI

@update:2016-06-28 00:44:46
@author:張三金 去哪兒網高級工程師
未完待續...緩存

1.引

若是要講dubbo源碼,那麼要從SPI開始(SPI自行google),由於dubbo全部的功能都是經過本身實現的一套SPI機制來擴展的,能夠理解爲dubbo的全部功能都拆分而且抽象爲interface,經過SPI查找這些interface的實現,而且經過url組合起來,就成了一個完整的rpc框架.app

2.ExtensionLoader

ExtensionLoader是dubbo內部的SPI實現,dubbo的全部組件都經過ExtensionLoader獲取,本文以最多見的interface Filter爲例,dubbo裏有不少的filter,而且支持碼農自定義filter,dubbo的filter全是經過SPI查找的,因此很是易於擴展.框架

com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper#buildInvokerChain方法中拿到當前service全部的filter的實現,而後執行.函數

List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);

2.1getExtensionLoader

getExtensionLoader方法會爲每個.class new 一個ExtensionLoader實例,並緩存起來ui

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

2.2getAdaptiveExtension

在dubbo中,須要擴展的功能須要在其interface上打到@SPI註解
在dubbo SPI中,interface的一個實現叫作Extension,Extension實現了這個接口的功能,一個interface有許多的Extensionthis

AdaptiveExtension叫作適配Extension,一個AdaptiveExtension的做用是從全部的Extension中,根據當前的url配置策略,選出指定的的Extensiongoogle

上面的代碼經過getAdaptiveExtension()方法拿到當前interface的AdaptiveExtensionurl

首先會拿到全部的接口實現
code:ExtensionLoader#loadExtensionClassesspa

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;
    }

loadExtensionClasses

  1. 從interface上拿到註解@SPI,取出默認實現的名字
  2. 調用loadFile加載interface的實現類

loadFile會到3個目錄下把全部的接口實現類加載進來

  1. META-INF/dubbo/internal/
    dubbo內部實現都放在這個目錄下
  2. META-INF/dubbo/
  3. META-INF/services/

例以下面爲dubbo內部實現filter的SPI文件
file:META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Filter

echo=com.alibaba.dubbo.rpc.filter.EchoFilter
generic=com.alibaba.dubbo.rpc.filter.GenericFilter
genericimpl=com.alibaba.dubbo.rpc.filter.GenericImplFilter
token=com.alibaba.dubbo.rpc.filter.TokenFilter
accesslog=com.alibaba.dubbo.rpc.filter.AccessLogFilter
activelimit=com.alibaba.dubbo.rpc.filter.ActiveLimitFilter
classloader=com.alibaba.dubbo.rpc.filter.ClassLoaderFilter
context=com.alibaba.dubbo.rpc.filter.ContextFilter
consumercontext=com.alibaba.dubbo.rpc.filter.ConsumerContextFilter
exception=com.alibaba.dubbo.rpc.filter.ExceptionFilter
executelimit=com.alibaba.dubbo.rpc.filter.ExecuteLimitFilter
deprecated=com.alibaba.dubbo.rpc.filter.DeprecatedFilter
compatible=com.alibaba.dubbo.rpc.filter.CompatibleFilter
timeout=com.alibaba.dubbo.rpc.filter.TimeoutFilter
monitor=com.alibaba.dubbo.monitor.support.MonitorFilter
validation=com.alibaba.dubbo.validation.filter.ValidationFilter
cache=com.alibaba.dubbo.cache.filter.CacheFilter
trace=com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter
future=com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter

2.3loadFile

SPI文件給出了全部實現的徹底限定類名,那麼能夠經過反射機制加載對應的Class

Class<?> clazz = Class.forName(line, true, classLoader);

加載的Class會被歸爲3類

  1. Extension,接口的實現,實現接口功能的類
    Holder<Map<String, Class<?>>> cachedClasses持有其引用
  2. AdaptiveExtension,適配指定Extension,打上了@Adaptive註解的類會被加載爲AdaptiveExtension,這個類只能有一個
    Class<?> cachedAdaptiveClass持有其引用
  3. wrapper,用裝飾者模式包裝Extension的類,wrapper有且只有一個以interface爲參數的構造函數
    Set<Class<?>> cachedWrapperClasses持有其引用

加載AdaptiveExtension,經過判斷@Adaptive註解

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());
            }
        }

加載wrapper extension

try {
            clazz.getConstructor(type);
            Set<Class<?>> wrappers = cachedWrapperClasses;
            if (wrappers == null) {
                cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
                wrappers = cachedWrapperClasses;
            }
            wrappers.add(clazz);
        } catch (NoSuchMethodException e) {
            //不是wrapper extension
        }

前面兩個都不是,那就是Extension,直接放入Holder<Map<String, Class<?>>> cachedClasses

3.註解

3.1@SPI

SPI註解標記在interface上,表示這個interface的實現經過SPI加載,value表示有多個實現時,默認使用指明的實現

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SPI {

    /**
     * 缺省擴展點名。
     */
    String value() default "";

}

reference

interface Filter

@SPI
public interface Filter {
    Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;

}
相關文章
相關標籤/搜索