【Dubbo】Adaptive

這里指的Adaptive是dubbo中的一個注解:@Adaptive。從這個注解的定義上我們可以看到@Target({ElementType.TYPE, ElementType.METHOD}),它表明@Adaptive可以用在類、接口和方法上。
@Adaptive代表dubbo的SPI的動態(tài)適應(yīng)能力,如果@Adaptive注解在擴(kuò)展點實現(xiàn)類上那個該擴(kuò)展點就是一個包裝真實擴(kuò)展點實例的裝飾類;如果注解在方法上那么擴(kuò)展點的實例就是一個動態(tài)代理類,例如Protocol$Adaptive對象。

package com.alibaba.dubbo.common.extension;
/**
 * Provide helpful information for {@link ExtensionLoader} to inject dependency extension instance.
 *
 * @see ExtensionLoader
 * @see URL
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
    /**
     * Decide which target extension to be injected. The name of the target extension is decided by the parameter passed
     * in the URL, and the parameter names are given by this method.
     * <p>
     * If the specified parameters are not found from {@link URL}, then the default extension will be used for
     * dependency injection (specified in its interface's {@link SPI}).
     * <p>
     * For examples, given <code>String[] {"key1", "key2"}</code>:
     * <ol>
     * <li>find parameter 'key1' in URL, use its value as the extension's name</li>
     * <li>try 'key2' for extension's name if 'key1' is not found (or its value is empty) in URL</li>
     * <li>use default extension if 'key2' doesn't appear either</li>
     * <li>otherwise, throw {@link IllegalStateException}</li>
     * </ol>
     * If default extension's name is not give on interface's {@link SPI}, then a name is generated from interface's
     * class name with the rule: divide classname from capital char into several parts, and separate the parts with
     * dot '.', for example: for {@code com.alibaba.dubbo.xxx.YyyInvokerWrapper}, its default name is
     * <code>String[] {"yyy.invoker.wrapper"}</code>. This name will be used to search for parameter from URL.
     *
     * @return parameter key names in URL
     */
    String[] value() default {};
}

dubbo為什么要設(shè)計adaptive?

adaptive設(shè)計的目的是為了識別固定已知類和擴(kuò)展未知類。

注解在類上和注解在方法上的區(qū)別?

注解在擴(kuò)展點實現(xiàn)類上:

代表人工實現(xiàn),實現(xiàn)一個裝飾類(設(shè)計模式中的裝飾模式),它主要作用于固定已知類,目前整個系統(tǒng)只有2個,AdaptiveCompiler、AdaptiveExtensionFactory。

  • a. 為什么AdaptiveCompiler這個類是固定已知的?
    因為整個框架僅支持Javassist和JdkCompiler;
  • b. 為什么AdaptiveExtensionFactory這個類是固定已知的?
    因為整個框架僅支持2個objFactory,一個是spi,另一個是spring;
    ExtensionLoader.getAdaptiveExtension方法會直接返回這個類的實例

注解在擴(kuò)展點接口方法上

代表自動生成和編譯一個動態(tài)的Adpative類,含有@Adaptive的方法中都可以根據(jù)方法參數(shù)動態(tài)獲取各自需要真實的擴(kuò)展點。它主要是用于SPI,因為spi的類是不固定、未知的擴(kuò)展類,所以設(shè)計了動態(tài)$Adaptive類;
ExtensionLoader.getAdaptiveExtension方法會返回動態(tài)編譯生成的$Adaptive

例如: Protocol的spi類有injvm、dubbo、registry、filter、listener等很多未知擴(kuò)展類,ExtensionLoader.getAdaptiveExtension會動態(tài)編譯Protocol$Adaptive的類,再通過在動態(tài)累的方法中調(diào)用ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(spi類);來提取對象。

Adaptive加載流程

在dubbo中一般會首先通過ExtensionLoader.getAdaptiveExtension獲取Adaptive擴(kuò)展。這個方法會首先在擴(kuò)展點接口的所有實現(xiàn)類中查找類上是否有含有@Adaptive注解,如果有這樣的類直接返回該類的實例,如果沒有則會查找擴(kuò)展點接口的方法是否有@Adaptive注解并動態(tài)編譯一個類實現(xiàn)該接口并擴(kuò)展這些含有@Adaptive注解的方法。

  • 代碼執(zhí)行流程
-----------------------getAdaptiveExtension()
-->getAdaptiveExtension()//為cachedAdaptiveInstance賦值
  -->createAdaptiveExtension()
    -->getAdaptiveExtensionClass()
      -->getExtensionClasses()//為cachedClasses 賦值
        -->loadExtensionClasses()
          -->loadFile
      -->createAdaptiveExtensionClass()//自動生成和編譯一個動態(tài)的adpative類,這個類是一個代理類
        -->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension()
        -->compiler.compile(code, classLoader)
    -->injectExtension()//作用:進(jìn)入IOC的反轉(zhuǎn)控制模式,實現(xiàn)了動態(tài)入注
  • 每次只需要從緩存中取出AdaptiveInstance,如果沒有命中則創(chuàng)建并加入緩存
     /**
     * @Author pengyunlong
     * @Description 每次只需要從緩存中取出AdaptiveInstance,如果沒有命中則創(chuàng)建并加入緩存
     * @param
     * @Date 2018/6/14 17:03
     */
    @SuppressWarnings("unchecked")
    public T getAdaptiveExtension() {
        Object instance = cachedAdaptiveInstance.get();
        if (instance == null) {
            if (createAdaptiveInstanceError == null) {
                synchronized (cachedAdaptiveInstance) {
                    instance = cachedAdaptiveInstance.get();
                    if (instance == null) {
                        try {
                            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;
    }
    /**
     * @Author pengyunlong 
     * @Description 獲取AdaptiveExtensionClass字節(jié)碼并創(chuàng)建實例然后IOC注入
     * @param 
     * @Date 2018/6/14 17:04
     */
    @SuppressWarnings("unchecked")
    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);
        }
    }
  • 加載實現(xiàn)該擴(kuò)展點接口的所有class并緩存,getExtensionClasses()會將類上有@Adaptive注解的類緩存到cachedAdaptiveClass中,如果cachedAdaptiveClass沒有值則需要動態(tài)編譯一個AdaptiveExtension
    /**
     * @Author pengyunlong
     * @Description 擴(kuò)展類上有@Adaptive注解的直接返回該類,如果沒有則動態(tài)創(chuàng)建
     * @param
     * @Date 2018/6/14 16:28
     */
    private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }
getExtensionClasses加載含有@Adaptive的擴(kuò)展點實現(xiàn)類的過程
 /**
     * @Author pengyunlong
     * @Description 從指定目錄讀取Spi配置文件,分析class并加入緩存
     *              1.cachedAdaptiveClass   含有Adaptive注解的class
     *              2.cachedWrapperClasses  含有指定參數(shù)的構(gòu)造方法的class
     *              3.cachedActivates       含有Activate注解的class
     *              4.cachedNames           其余的class
     * @param
     * @Date 2018/6/7 17:13
     */
    private void loadFile(Map<String, Class<?>> extensionClasses, String dir){
        String fileName = dir + type.getName();
      //遍歷SPI文件的每一行配置
     while ((line = reader.readLine()) != null) {
              Class<?> clazz = Class.forName(line, true, classLoader);
         //擴(kuò)展點實例類上有@Adaptive注解直接設(shè)置cachedAdaptiveClass
              if (clazz.isAnnotationPresent(Adaptive.class)) {
                        if(cachedAdaptiveClass == null) {
                           cachedAdaptiveClass = clazz;
             } else if (! cachedAdaptiveClass.equals(clazz)) {
                         throw new 
            }
            try {
               clazz.getConstructor(type);
               //如果擴(kuò)展點實例的是含有type類型參數(shù)的構(gòu)造方法則加入cachedWrapperClasses集合中
               Set<Class<?>> wrappers = cachedWrapperClasses;
                if (wrappers == null) {
                cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
                wrappers = cachedWrapperClasses;
              }
              wrappers.add(clazz);
           } catch (NoSuchMethodException e) {
              Activate activate = clazz.getAnnotation(Activate.class);
              if (activate != null) {
                  cachedActivates.put(names[0], activate);
               }
               //其余情況則將擴(kuò)展點類名加入cachedNames,key為class
               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 
                        }
                  }
            }
      }
    }

關(guān)于loadfile的一些細(xì)節(jié)

  • cachedAdaptiveClass
    如果這個class含有adative注解就賦值,例如ExtensionFactory,而例如Protocol在這個環(huán)節(jié)是沒有的。
  • cachedWrapperClasses
    只有當(dāng)該class無adative注解,并且構(gòu)造函數(shù)包含目標(biāo)接口(type)類型,例如protocol里面的spi就只有ProtocolFilterWrapper和ProtocolListenerWrapper能命中
  • cachedActivates
    剩下的類,包含Activate注解
  • cachedNames
    剩下的類就存儲在這里。
動態(tài)編譯過程
  • 沒有含有@Adaptive注解的擴(kuò)展點實現(xiàn)類則調(diào)用createAdaptiveExtensionClassCode動態(tài)生成AdaptiveExtension的代碼然后編譯默認(rèn)采用Javassist編譯
/**
     * @Author pengyunlong 
     * @Description 動態(tài)生成代碼然后編譯
     * @param 
     * @Date 2018/6/14 16:38
     */
    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);
    }
  • createAdaptiveExtensionClassCode使用的模板文件adaptive-code-teamplet代碼模板
package <擴(kuò)展點接口所在包>;
 
public class <擴(kuò)展點接口名>$Adpative implements <擴(kuò)展點接口> {
    public <有@Adaptive注解的接口方法>(<方法參數(shù)>) {
        if(是否有URL類型方法參數(shù)?) 使用該URL參數(shù)
        else if(是否有方法類型上有URL屬性) 使用該URL屬性
        # <else 在加載擴(kuò)展點生成自適應(yīng)擴(kuò)展點類時拋異常,即加載擴(kuò)展點失??!>
         
        if(獲取的URL == null) {
            throw new IllegalArgumentException("url == null");
        }
 
              根據(jù)@Adaptive注解上聲明的Key的順序,從URL獲致Value,作為實際擴(kuò)展點名。
               如URL沒有Value,則使用缺省擴(kuò)展點實現(xiàn)。如沒有擴(kuò)展點, throw new IllegalStateException("Fail to get extension");
 
               在擴(kuò)展點實現(xiàn)調(diào)用該方法,并返回結(jié)果。
    }
 
    public <有@Adaptive注解的接口方法>(<方法參數(shù)>) {
        throw new UnsupportedOperationException("is not adaptive method!");
    }
}
  • ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();因為Compiler.class的擴(kuò)展點實現(xiàn)類中AdaptiveCompiler又@Adaptive注解所以的返回值為AdaptiveCompiler,默認(rèn)使用JavassistCompiler來解析并編譯上一步生成好的代碼;
@Adaptive
public class AdaptiveCompiler implements Compiler {

    private static volatile String DEFAULT_COMPILER;

    public static void setDefaultCompiler(String compiler) {
        DEFAULT_COMPILER = compiler;
    }

    public Class<?> compile(String code, ClassLoader classLoader) {
        Compiler compiler;
        ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
        String name = DEFAULT_COMPILER; // copy reference
        if (name != null && name.length() > 0) {
            compiler = loader.getExtension(name);
        } else {
            compiler = loader.getDefaultExtension();
        }
        return compiler.compile(code, classLoader);
    }
}
package com.alibaba.dubbo.common.compiler.support;
/**
 * JavassistCompiler. (SPI, Singleton, ThreadSafe)
 */
public class JavassistCompiler extends AbstractCompiler {
    ......
    private static final Pattern IMPORT_PATTERN = Pattern.compile("import\\s+([\\w\\.\\*]+);\n");
    @Override
    public Class<?> doCompile(String name, String source) throws Throwable {
       ......
        String[] packages = importPackages.toArray(new String[0]);
        matcher = EXTENDS_PATTERN.matcher(source);
        CtClass cls;
        if (matcher.find()) {
            String extend = matcher.group(1).trim();
            String extendClass;
            if (extend.contains(".")) {
                extendClass = extend;
            } else if (fullNames.containsKey(extend)) {
                extendClass = fullNames.get(extend);
            } else {
                extendClass = ClassUtils.forName(packages, extend).getName();
            }
            cls = pool.makeClass(name, pool.get(extendClass));
        } else {
            cls = pool.makeClass(name);
        }
        ......
        return cls.toClass(ClassHelper.getCallerClassLoader(getClass()), JavassistCompiler.class.getProtectionDomain());
    }
}
動態(tài)編譯結(jié)果

Protocol所有擴(kuò)展實現(xiàn)類上都沒有@Adaptive注解,且擴(kuò)展接口含有兩個 @Adaptive 注解的方法:exporter() refer(),所以dubbo會生成一個動態(tài)類Protocol$Adaptive,且它實現(xiàn)Protocol接口來擴(kuò)展這兩個Adaptive方法。擴(kuò)展點接口和最終動態(tài)生成Protocol$Adaptive類如下:

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

解釋一下export(com.alibaba.dubbo.rpc.Invoker arg0)方法:

  1. String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
    從arg0中解析出擴(kuò)展點名稱extName,extName的默認(rèn)值為@SPI的value。這是adaptive的精髓:每一個方法都可以根據(jù)方法參數(shù)動態(tài)獲取各自需要的擴(kuò)展點。
  2. Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
    根據(jù)extName重新獲取指定的Protocol.class擴(kuò)展點。如果所有擴(kuò)展點中含有Wrapper(listener,fiter)則ExtensionLoader.getExtension()會將真正的實現(xiàn)類通過Wrapper(listener,fiter)包裝后返回。
  3. extension.export(arg0)
    執(zhí)行目標(biāo)類的目標(biāo)方法

這樣就實現(xiàn)了每一個方法都可以根據(jù)方法參數(shù)動態(tài)獲取各自需要真實的擴(kuò)展點。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容