Dubbo SPI 之 @Adaptive 固定已知類擴展未知類

咱們已知Adaptive是一個註解,經過 @Target({ElementType.TYPE, ElementType.METHOD}) 可知該註解能夠用在類定義或方法定義上。java

/**
 * Activate
 * <p/>
 * 對於能夠被框架中自動激活加載擴展,此Annotation用於配置擴展被自動激活加載條件。
 * 好比,過濾擴展,有多個實現,使用Activate Annotation的擴展能夠根據條件被自動加載。
 * <ol>
 * <li>{@link Activate#group()}生效的Group。具體的有哪些Group值由框架SPI給出。
 * <li>{@link Activate#value()}在{@link com.alibaba.dubbo.common.URL}中Key集合中有,則生效。
 * </ol>
 * <p>
 * <p/>
 * 底層框架SPI提供者經過{@link com.alibaba.dubbo.common.extension.ExtensionLoader}的{@link ExtensionLoader#getActivateExtension}方法
 * 得到條件的擴展。
 *
 * @author william.liangf
 * @author ding.lid
 * @export
 * @see SPI
 * @see ExtensionLoader
 * @see ExtensionLoader#getActivateExtension(com.alibaba.dubbo.common.URL, String[], String)
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
    /**
     * Group過濾條件。
     * <br />
     * 包含{@link ExtensionLoader#getActivateExtension}的group參數給的值,則返回擴展。
     * <br />
     * 如沒有Group設置,則不過濾。
     */
    String[] group() default {};

    /**
     * Key過濾條件。包含{@link ExtensionLoader#getActivateExtension}的URL的參數Key中有,則返回擴展。
     * <p/>
     * 示例:<br/>
     * 註解的值 <code>@Activate("cache,validatioin")</code>,
     * 則{@link ExtensionLoader#getActivateExtension}的URL的參數有<code>cache</code>Key,或是<code>validatioin</code>則返回擴展。
     * <br/>
     * 如沒有設置,則不過濾。
     */
    String[] value() default {};

    /**
     * 排序信息,能夠不提供。
     */
    String[] before() default {};

    /**
     * 排序信息,能夠不提供。
     */
    String[] after() default {};

    /**
     * 排序信息,能夠不提供。
     */
    int order() default 0;
}

它的設計目的是爲了實現Dubbo SPI 時用來固定已知的類和擴展未知類。spring

註解在接口的實現類上和方法上的區別:

1.註解在接口的實現類上:表明人工實現,實現一個裝飾類,它主要用於固定已知類,目前整個系統只有兩個,AdaptiveCompiler、AdaptiveExtensionFactory。設計模式

@SPI("javassist")
public interface Compiler {

    /**
     * Compile java source code.
     *
     * @param code        Java source code
     * @param classLoader TODO
     * @return Compiled class
     */
    Class<?> compile(String code, ClassLoader classLoader);

}

項目繼承圖以下:app

a. 爲何AdaptiveCompiler這個類是固定已知的?
由於整個框架僅支持Javassist和JdkCompiler;
b. 爲何AdaptiveExtensionFactory這個類是固定已知的?
由於整個框架僅支持2個objFactory,一個是spi,另外一個是spring;框架

2.註解在方法上,表明自動生成和編譯一個動態的Adaptive類,每一個方法均可以根據方法參數動態獲取各自的擴展點,主要因爲SPI 獲取類爲不固定的位置的擴展類,因此設計了動態的$Adaptive類。jvm

例如 Protocol的spi類有injvm、dubbo、registry、filter、listener等不少未知擴展類,它設計了Protocol$Adaptive的類,再經過ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(spi類);來提取對象。ide

在dubbo中通常會首先經過ExtensionLoader.getAdaptiveExtension獲取Adaptive擴展。這個方法會首先在擴展點接口的全部實現類中查找類上是否有含有@Adaptive註解,若是有這樣的類直接返回該類的實例,若是沒有則會查找擴展點接口的方法是否有@Adaptive註解並動態編譯一個類實現該接口並擴展這些含有@Adaptive註解的方法。函數

代碼執行流程:ui

-----------------------getAdaptiveExtension()
-->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的反轉控制模式,實現了動態入注

Protocol全部擴展實現類上都沒有@Adaptive註解,且擴展接口含有兩個 @Adaptive 註解的方法:exporter() refer(),因此dubbo會生成一個動態類Protocol$Adaptive,且它實現Protocol接口來擴展這兩個Adaptive方法。擴展點接口和最終動態生成Protocol$Adaptive類以下:this

@SPI("dubbo")
public interface Protocol {
    int getDefaultPort();

    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
    void destroy();

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

String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );從arg0中解析出擴展點名稱extName,extName的默認值爲@SPI的value。這是adaptive的精髓:每個方法均可以根據方法參數動態獲取各自須要的擴展點。

Protocol extension =(Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).
getExtension(extName);根據extName從新獲取指定的Protocol.class擴展點。若是全部擴展點中含有Wrapper(listener,fiter)則ExtensionLoader.getExtension()會將真正的實現類經過Wrapper(listener,fiter)包裝後返回。

 

Dubbo擴展點AOP功能

對dubbo擴展點作切面功能的擴展,從ExtensionLoader的createExtension() 代碼提及:

@SuppressWarnings("unchecked")
    private T createExtension(String name) {
        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, (T) clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            injectExtension(instance);


            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            //begin 
            if (wrapperClasses != null && wrapperClasses.size() > 0) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            //end 
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                    type + ")  could not be instantiated: " + t.getMessage(), t);
        }
    }

關鍵說明, 
1. cachedWrapperClasses是在loadFile裏面加載的,」WrapperClass」是符合某種特徵的擴展接口實現類的稱呼。例如ProtocolFilterWrapper 
和ProtocolListenerWrapper。他們共同特徵就是帶有Protocol接口的構造函數。

/**
 * ListenerProtocol
 *
 * @author william.liangf
 */
public class ProtocolFilterWrapper implements Protocol {

    private final Protocol protocol;

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

    private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
        Invoker<T> last = invoker;
        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
        if (filters.size() > 0) {
            for (int i = filters.size() - 1; i >= 0; i--) {
                final Filter filter = filters.get(i);
                final Invoker<T> next = last;
                last = new Invoker<T>() {

                    public Class<T> getInterface() {
                        return invoker.getInterface();
                    }

                    public URL getUrl() {
                        return invoker.getUrl();
                    }

                    public boolean isAvailable() {
                        return invoker.isAvailable();
                    }

                    public Result invoke(Invocation invocation) throws RpcException {
                        return filter.invoke(next, invocation);
                    }

                    public void destroy() {
                        invoker.destroy();
                    }

                    @Override
                    public String toString() {
                        return invoker.toString();
                    }
                };
            }
        }
        return last;
    }

    public int getDefaultPort() {
        return protocol.getDefaultPort();
    }

    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            return protocol.export(invoker);
        }
        return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
    }

    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
            return protocol.refer(type, url);
        }
        return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
    }

    public void destroy() {
        protocol.destroy();
    }

}
  1. instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); 就是將拿到的instance放到一個包裝類中,而後通過一層包裝以後,在放到另一個包裝類中, 
    經過這種方式dubbo實現了擴展點的AOP

遇到的設計模式

  1. 裝飾器模式
  2. 動態代理模式
相關文章
相關標籤/搜索