Dubbo-自適應拓展機制

背景

    在 Dubbo 中,不少拓展都是經過 SPI 機制進行加載的,好比 Protocol、Cluster、LoadBalance 等,這些都是Dubbo的基礎組件。這些基礎組件的拓展不是在系統框架啓動階段被加載,而是拓展方法被調用時,根據運行時參數(URL)進行動態加載的。自適應拓展機制是創建在SPI基礎之上的,自適應拓展類的核心實現:在拓展接口的方法被調用時,經過SPI加載具體的拓展實現類,並調用拓展對象的同名方法。能夠看到自適應拓展是創建在SPI基礎上的實現的功能,自適應拓展加上SPI實現Dubbo可拓展和解耦,這是Dubbo很是重要機制。java

    自適應拓展類的過程是:選根據接口方法上的"@Adaptive" 註解生成自適應代碼,而後經過Javassist編譯建立自適應拓展類代碼(使用系統內置特殊的自適應拓展類),再經過反射建立自適應拓展類的實例,而且保存至緩存。須要注意系統內置特殊的自適應拓展類是,分別是 AdaptiveCompiler 和 AdaptiveExtensionFactory,由於這兩個類的類被 Adaptive 註解了(是類被註解,而不是方法)。Dubbo這樣設計的緣由是:在產生自適應拓展類代碼時,爲了不死循環產生自適應拓展類代碼,這一點很重要,特別是新手很容易搞混淆。apache

代碼分析

建立自適應拓展類實例-邏輯圖

    這裏以"org.apache.dubbo.rpc.Protocol"做爲示例講解下建立自適應拓展類的邏輯,由於這部分功能邏輯單純從代碼上看很容易搞錯順序或關係,因此單獨拿出來解釋下。設計模式

    代碼入口是"ExtensionLoader.getExtensionLoader"和SPI章節入口是同樣的,但咱們要了解的內容不同。下面是獲取ExtensionLoader實例的代碼在SPI章節講解過,這裏就不囉嗦了。緩存

@SuppressWarnings("unchecked")
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    logger.info("type:"+type);
    //空值判斷
    if (type == null) {
        throw new IllegalArgumentException("Extension type == null");
    }

    //判斷是否接口類
    if (!type.isInterface()) {
        throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
    }
    // 即:type.isAnnotationPresent(SPI.class),是否包含SPI註解
    if (!withExtensionAnnotation(type)) {
        throw new IllegalArgumentException("Extension type (" + type +
                ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
    }

    //new ExtensionLoader()時,
    //觸發ExtensionFactory的ExtensionLoader
    // 實例建立:ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    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;
}

    只是有一點,ExtensionLoader的私用構造方法還有其它代碼邏輯,也就是在初始化其它擴展類的ExtensionLoader實例以前,必須先初始化ExtensionFactory.class的ExtensionLoader實例。app

private ExtensionLoader(Class<?> type) {
    this.type = type;
    //先執行:ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension(),即獲得AdaptiveExtensionFactory
    //ExtensionFactory的objectFactory屬性爲空
    //非ExtensionFactory的objectFactory屬性爲AdaptiveExtensionFactory
    objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

    ExtensionFactory.class在獲取自適應拓展類實例時,由於ExtensionFactory.class的字類AdaptiveExtensionFactory有「@Adaptive」,因此AdaptiveExtensionFactory直接返回而且成爲自適應拓展類,這是特殊的例子。框架

    在獲取自適應拓展類實例,先是從緩存獲取,若是緩存沒有命中,再建立。由於建立一個自適應拓展類實例太耗資源和時間了,因此這些對象都是以單例方式運行。dom

@SuppressWarnings("unchecked")
public T getAdaptiveExtension() {
    logger.info("getAdaptiveExtension.type:"+type);
    // 從緩存中獲取自適應拓展
    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("Failed to create adaptive instance: " + t.toString(), t);
                    }
                }
            }
        } else {
            throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
        }
    }

    return (T) instance;
}

    這裏和SPI同樣,均使用了Dubbo的IOC,在注入依賴以前先獲取自適應類,並建立實例。ide

@SuppressWarnings("unchecked")
private T createAdaptiveExtension() {
    try {
        //獲取自適應拓展類,而後建立實例,並經過反射注入該實例的依賴(Dubbo的IOC實現)
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
    }
}

這個方法包含三個邏輯性能

  • 先加載SPI 全部的拓展類,這個SPI章節詳細展開過。
  • 判斷cachedAdaptiveClass屬性是否爲空,若是不爲空直接返回cachedAdaptiveClass,即自適應拓展類爲cachedAdaptiveClass。cachedAdaptiveClass就表明着那些拓展類上面標註"@Adaptive"註釋。
  • 建立自適應擴展類的代碼,而後經過Javassist編譯,並經過反射獲得自適應拓展對象。
private Class<?> getAdaptiveExtensionClass() {
    // 獲取全部的拓展類
    getExtensionClasses();
    // 加載完全部的實現以後,若是發現有cachedAdaptiveClass不爲空,則直接返回緩存,
    // 同時也意味着自拓展適應類:cachedAdaptiveClass是AdaptiveExtensionFactory或AdaptiveCompiler
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    // 建立自適應拓展類
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

    先獲取指定類(如:"org.apache.dubbo.rpc.Protocol")全部的拓展類,在獲取拓展類時,判斷拓展類是否標註了"@Adaptive",若是標註了"@Adaptive"表示當前類爲自適應拓展類,不須要經過動態生成代碼建立自適應拓展類。如:AdaptiveExtensionFactory和AdaptiveCompiler。ui

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
    //檢查clazz(擴展實現類)是否type(接口)的實現類
    if (!type.isAssignableFrom(clazz)) {
        throw new IllegalStateException("Error occurred when loading extension class (interface: " +
                type + ", class line: " + clazz.getName() + "), class "
                + clazz.getName() + " is not subtype of interface.");
    }
    // 檢測目標類上是否有 Adaptive 註解,若是有則設置 cachedAdaptiveClass緩存
    if (clazz.isAnnotationPresent(Adaptive.class)) {
        cacheAdaptiveClass(clazz);
    // 檢測 clazz 是不是 Wrapper 類型,若是有則存儲 clazz 到 cachedWrapperClasses 緩存中
    } else if (isWrapperClass(clazz)) {
        //判斷是不是wrapper類型(包裝類型),實現類的構造方法中的參數是擴展點類型的,就是一個Wrapper類
        cacheWrapperClass(clazz);
    } else {// 程序進入此分支,代表 clazz 是一個普通的拓展類
        // 獲取默認的構造方法
        clazz.getConstructor();
        if (StringUtils.isEmpty(name)) {//name即Key值,如:spi=org.apache.dubbo.common.extension.factory.SpiExtensionFactory
            // 若是 name 爲空,則嘗試從 Extension 註解中獲取 name,或使用小寫的類名做爲 name
            name = findAnnotationName(clazz);
            if (name.length() == 0) {
                throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
            }
        }
        String[] names = NAME_SEPARATOR.split(name);// 切分 name
        if (ArrayUtils.isNotEmpty(names)) {
            cacheActivateClass(clazz, names[0]);
            for (String n : names) {
                cacheName(clazz, n);// 存儲 Class 到名稱的映射關係
                saveInExtensionClass(extensionClasses, clazz, n);//緩存Name與Clazz映謝關係
            }
        }
    }
}
private void cacheAdaptiveClass(Class<?> clazz) {
    if (cachedAdaptiveClass == null) {
        // 設置 cachedAdaptiveClass緩存,並且這個類只有一個,也就是在指定的擴展類範圍只能有一個類能夠標註「@Adaptive」
        cachedAdaptiveClass = clazz;
    } else if (!cachedAdaptiveClass.equals(clazz)) {
        throw new IllegalStateException("More than 1 adaptive class found: "
                + cachedAdaptiveClass.getClass().getName()
                + ", " + clazz.getClass().getName());
    }
}

    若是全部拓展實現類都沒有合適標註"@Adaptive",那建立自適應拓展類的代碼。這個方法詳細解釋了爲何須要 AdaptiveCompiler 和 AdaptiveExtensionFactory的緣由。

private Class<?> createAdaptiveExtensionClass() {
    // 構建自適應拓展代碼
    String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
    // 獲取類加載器
    ClassLoader classLoader = findClassLoader();
    //dubbo默認使用javassist獲取編譯器
    //若是沒有AdaptiveExtensionFactory和AdaptiveCompiler這兩個類,那cachedAdaptiveClass一直爲空,這個方法就會一直是死循環
    //這也是Dubbo有會把@Adaptive標註類上面的緣由。
    //解析Compiler的實現類的時候,會在getAdaptiveExtensionClass中直接返回AdaptiveCompiler,不須要動態生成代碼
    org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.
            getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();

    // 編譯代碼,生成 Class
    return compiler.compile(code, classLoader);
}

生成自適應拓展代碼-邏輯圖

    產生自適應拓展類代碼的邏輯比較複雜,涉及到的細節也比較多,這裏以"org.apache.dubbo.rpc.Protocol"產生的適應拓展類代碼做爲例子,方便在閱讀源碼時做爲對比,方便理解。

package org.apache.dubbo.rpc;


import org.apache.dubbo.common.extension.ExtensionLoader;

public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
    public void destroy() {
        throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }

    public int getDefaultPort() {
        throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }

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

    public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null)
            throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
        org.apache.dubbo.common.URL url = arg0.getUrl();
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
        org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.export(arg0);
    }
}

    要產生自適應拓展類的接口至少有一個方法被標註了"@Adaptive",不然不產生代任何碼。由於在這個方法前面已經排除類的標註了,這裏只須要確認方法有註解就好了。

public String generate() {
    //Dubbo 要求該接口至少有一個方法被 Adaptive 註解修飾
    if (!hasAdaptiveMethod()) {
        throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
    }

    StringBuilder code = new StringBuilder();
    code.append(generatePackageInfo());//生成包代碼,如:package org.apache.dubbo.rpc;
    code.append(generateImports());//生成import代碼,如import org.apache.dubbo.common.extension.ExtensionLoader;
    code.append(generateClassDeclaration());//生成聲明類代碼,如:public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {

    // 經過反射獲取全部的方法
    Method[] methods = type.getMethods();
    // 遍歷方法列表
    for (Method method : methods) {
        code.append(generateMethod(method));
    }
    code.append("}");

    if (logger.isDebugEnabled()) {
        logger.debug(code.toString());
    }
    logger.info("\n\n  code.toString:"+code.toString());
    return code.toString();
}

    產生代碼的重點在於方法,產生方法的時須要根據參數判斷和邏輯處理,相對複雜。

/**
 * generate method declaration
 * 生成"方法"代碼邏輯
 */
private String generateMethod(Method method) {
    //返回參數類型,如:org.apache.dubbo.rpc.Exporter
    String methodReturnType = method.getReturnType().getCanonicalName();
    //方法名代碼,如:export
    String methodName = method.getName();
    //方法內容代碼
    String methodContent = generateMethodContent(method);
    //方法參數代碼,如:org.apache.dubbo.rpc.Invoker arg0
    String methodArgs = generateMethodArguments(method);
    //拋異常代碼,如:throws org.apache.dubbo.rpc.RpcException
    String methodThrows = generateMethodThrows(method);
    return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent);
}

    產生方法代碼時,首先產生是沒有標註 "@Adaptive"方法的邏輯,而後須要直接處理參數URL或間接處理URL參數,如:判斷是否爲空,判斷參數的屬性getURL方法等。若是全部參數都不直接包含URL或間接包含URL參數,就直接拋異常。URL是Dubbo在每一個層流轉過程當中,用於上傳下達傳遞參數的,是一個很重要的數據類型。

private String generateMethodContent(Method method) {
    //獲取方法上Adaptive註解
    Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
    StringBuilder code = new StringBuilder(512);
    //沒有標註 Adaptive 註解的方法生成代理邏輯:僅會生成一句拋出異常的代碼。
    // throw new UnsupportedOperationException("The method
    if (adaptiveAnnotation == null) {
        return generateUnsupported(method);
    } else {
        // 遍歷參數列表,肯定 org.apache.dubbo.common.URL在方法參數位置
        int urlTypeIndex = getUrlTypeIndex(method);

        // urlTypeIndex != -1,表示參數列表中存在 URL 參數
        // found parameter in URL type
        if (urlTypeIndex != -1) {
            // 爲 URL 類型參數生成判空代碼,格式以下:
            // if (arg1 == null) throw new IllegalArgumentException("url == null");
            code.append(generateUrlNullCheck(urlTypeIndex));
        } else {
            // did not find parameter in URL type
            //若是參數不包括URL,那從參數的屬性應該包含URL,若是參數的屬性不包含URL屬性,那拋異常,如:
            //if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
            // if (arg0.getUrl() == null)
            //  throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
            code.append(generateUrlAssignmentIndirectly(method));
        }

        //獲取 Adaptive 註解值,若是不存在,採用類名,如:LoadBalance 通過處理後,獲得 load.balance
        String[] value = getMethodAdaptiveValue(adaptiveAnnotation);

        //檢測方法列表中是否存在 org.apache.dubbo.rpc.Invocation 類型的參數
        boolean hasInvocation = hasInvocationArgument(method);

        // append代碼:爲Invocation 類型參數生成判空代碼
        code.append(generateInvocationArgumentNullCheck(method));

        //append代碼:生成拓展名獲取邏輯代碼
        // String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
        code.append(generateExtNameAssignment(value, hasInvocation));

        // 生成 extName 判空代碼
        //if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
        code.append(generateExtNameNullCheck(value));

        //生成拓展獲取代碼
        //如:org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
        code.append(generateExtensionAssignment());

        //生成返回值代碼
        //return extension.export(arg0);
        code.append(generateReturnAndInvocation(method));
    }

    return code.toString();
}

    經過反射方法獲取方法名稱、訪問權限、方法參數數量,而後判斷參數是否包括getUrl方法。

private String generateUrlAssignmentIndirectly(Method method) {
    Class<?>[] pts = method.getParameterTypes();

    // 遍歷方法的參數類型列表
    // find URL getter method
    for (int i = 0; i < pts.length; ++i) {
        // 獲取某一類型參數的所有方法
        for (Method m : pts[i].getMethods()) {
            // 遍歷方法列表,尋找可返回 URL 的 getter 方法
            String name = m.getName();
            // 1. 方法名以 get 開頭,或方法名大於3個字符
            // 2. 方法的訪問權限爲 public
            // 3. 非靜態方法
            // 4. 方法參數數量爲0
            // 5. 方法返回值類型爲 URL
            if ((name.startsWith("get") || name.length() > 3)
                    && Modifier.isPublic(m.getModifiers())
                    && !Modifier.isStatic(m.getModifiers())
                    && m.getParameterTypes().length == 0
                    && m.getReturnType() == URL.class) {
                return generateGetUrlNullCheck(i, pts[i], name);
            }
        }
    }

    // 若是全部參數中均不包含可返回 URL 的 getter 方法,則拋出異常
    // getter method not found, throw
    throw new IllegalStateException("Failed to create adaptive class for interface " + type.getName()
                    + ": not found url parameter or url attribute in parameters of method " + method.getName());

}

    生成拓展名獲取邏輯代碼,這部分代碼比較複雜,判斷條件分支也多,須要屢次斷點而且對照已產生的代碼一塊兒閱讀比較容易理解。

private String generateExtNameAssignment(String[] value, boolean hasInvocation) {
    String getNameCode = null;
    // 遍歷 value,這裏的 value 是 Adaptive 的註解值。
    // 此處循環目的是生成從 URL 中獲取拓展名的代碼,生成的代碼會賦值給 getNameCode 變量。注意這
    // 個循環的遍歷順序是由後向前遍歷的。
    for (int i = value.length - 1; i >= 0; --i) {
        if (i == value.length - 1) {
            // 默認拓展名非空
            if (null != defaultExtName) {
                // protocol 是 url 的一部分,可經過 getProtocol 方法獲取,其餘的則是從
                // URL 參數中獲取。由於獲取方式不一樣,因此這裏要判斷 value[i] 是否爲 protocol
                if (!"protocol".equals(value[i])) {
                    // hasInvocation 用於標識方法參數列表中是否有 Invocation 類型參數
                    if (hasInvocation) {
                        // 生成的代碼功能等價於下面的代碼:
                        //   url.getMethodParameter(methodName, value[i], defaultExtName)
                        // 以 LoadBalance 接口的 select 方法爲例,最終生成的代碼以下:
                        //   url.getMethodParameter(methodName, "loadbalance", "random")
                        getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
                    } else {
                        // 生成的代碼功能等價於下面的代碼:
                        //   url.getParameter(value[i], defaultExtName)
                        getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);
                    }
                } else {
                    // 生成的代碼功能等價於下面的代碼:
                    //   ( url.getProtocol() == null ? defaultExtName : url.getProtocol() )
                    getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName);
                }
            } else {// 默認拓展名爲空
                if (!"protocol".equals(value[i])) {
                    if (hasInvocation) {
                        // 生成代碼格式同上
                        getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
                    } else {
                        // 生成的代碼功能等價於下面的代碼:
                        //   url.getParameter(value[i])
                        getNameCode = String.format("url.getParameter(\"%s\")", value[i]);
                    }
                } else {
                    // 生成從 url 中獲取協議的代碼,好比 "dubbo"
                    getNameCode = "url.getProtocol()";
                }
            }
        } else {
            if (!"protocol".equals(value[i])) {
                if (hasInvocation) {
                    // 生成代碼格式同上
                    getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
                } else {
                    // 生成的代碼功能等價於下面的代碼:
                    //   url.getParameter(value[i], getNameCode)
                    // 以 Transporter 接口的 connect 方法爲例,最終生成的代碼以下:
                    //   url.getParameter("client", url.getParameter("transporter", "netty"))
                    getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode);
                }
            } else {
                // 生成的代碼功能等價於下面的代碼:
                //   url.getProtocol() == null ? getNameCode : url.getProtocol()
                // 以 Protocol 接口的 connect 方法爲例,最終生成的代碼以下:
                //   url.getProtocol() == null ? "dubbo" : url.getProtocol()
                getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode);
            }
        }
    }

    // 生成 extName 賦值代碼
    return String.format(CODE_EXT_NAME_ASSIGNMENT, getNameCode);
}

    獲得一個完整的自適應拓展類後,自適應拓展類的核心邏輯:在拓展接口的方法被調用時,基於SPI機制而且根據URL或間接的URL參數加載具體的拓展實現類,並調用拓展對象的同名方法。

    最後獲得的是String類型自適應拓展類代碼,根據String代碼建立Class對象和實現,這時候就是輪到Javassit出場了。

@Adaptive
public class AdaptiveCompiler implements Compiler {

    private static volatile String DEFAULT_COMPILER;

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

    @Override
    public Class<?> compile(String code, ClassLoader classLoader) {
        Compiler compiler;
        //獲得一個ExtensionLoader
        ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
        //默認的Compiler名字
        String name = DEFAULT_COMPILER; // copy reference
        if (name != null && name.length() > 0) {
            compiler = loader.getExtension(name);
        } else {
            //若是沒有設置編譯器的擴展名,就使用默認編譯器
            //默認值:org.apache.dubbo.common.compiler.support.JavassistCompiler
            compiler = loader.getDefaultExtension();
        }

        //擴展實現類來編譯代碼
        return compiler.compile(code, classLoader);
    }
}

涉及的知識點

    本章節涉及的知識點有字節碼技術,Java主要流行的字節碼操縱框架有Javassist 和 ASM。字節碼技術主要是Dubbo默認是使用Javassist字節碼,Dubbo使用Javassist是基於性能和易用性兩方面考慮的。具體能夠參考早期 dubbo 的做者梁飛的博客 http://javatar.iteye.com/blog/814426,從這裏面也能夠知道技術選型的過程和考量點。

    本章節涉及的知識點還有設計模式-代理模式,代理模式在Dubbo中是普遍應用的,Dubbo並無直接使用JDK的代理技術,而是經過Javassist實現代理。代理模式在Dubbo、Spring都是普遍應用, 特別是Dubbo的服務導入與導出部分大量使用。

小結

    本章節涉及到自適應拓展類機制,是Dubbo的重要的基礎機制。基於Dubbo的SPI的自適應拓展機制能夠動態調用拓展實現類,不少基礎組件都依賴些機制Protocol、Cluster、LoadBalance 等。

相關文章
相關標籤/搜索