dubbo的自適應擴展機制

註解@Adaptive

在dubbo中,爲了靈活的適配一個接口的多種實現,而不是經過硬編碼來指定用哪一個實現,提供了@Adaptive註解。這個註解看的出,在類以及方法上使用的。java

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
    
    String[] value() default {};
}

源碼分析

getAdaptiveExtension

從緩存中獲取,沒有則建立apache

public T getAdaptiveExtension() {
    Object instance = cachedAdaptiveInstance.get();
    // 緩存爲空
    if (instance == null) {
        if (createAdaptiveInstanceError != null) {
            throw new IllegalStateException("Failed to create adaptive instance: " +
                    createAdaptiveInstanceError.toString(),
                    createAdaptiveInstanceError);
        }
        // 建立
        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);
                }
            }
        }
    }

    return (T) instance;
}

createAdaptiveExtension

private T createAdaptiveExtension() {
    try {
        // 獲取擴展對象的實例,再注入
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
    }
}

getAdaptiveExtensionClass

private Class<?> getAdaptiveExtensionClass() {
    // 獲取SPI信息
    getExtensionClasses();
    // 緩存有,則返回
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    // 沒有則建立
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

createAdaptiveExtensionClass

private Class<?> createAdaptiveExtensionClass() {
    // 拼接代碼
    String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
    // 獲取類加載器
    ClassLoader classLoader = findClassLoader();
    // 獲取編譯器,javassist用於動態編程
    org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    // 把代碼編譯成class
    return compiler.compile(code, classLoader);
}

爲了方便後面源碼參考,先貼出拼接好的代碼(我這邊以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.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);
    }

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

AdaptiveClassCodeGenerator.generate

public String generate() {
    // no need to generate adaptive class since there's no adaptive method found.
    // 至少一個方法是有Adaptive註解
    if (!hasAdaptiveMethod()) {
        throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
    }

    StringBuilder code = new StringBuilder();
    // 拼接package信息
    code.append(generatePackageInfo());
    // 拼接Imports信息
    code.append(generateImports());
    // 拼接類名以及實現的接口
    code.append(generateClassDeclaration());
    // 獲取全部方法,拼接方法
    Method[] methods = type.getMethods();
    for (Method method : methods) {
        code.append(generateMethod(method));
    }
    code.append("}");

    if (logger.isDebugEnabled()) {
        logger.debug(code.toString());
    }
    return code.toString();
}

AdaptiveClassCodeGenerator.generateMethod

private String generateMethod(Method method) {
    // 返回值
    String methodReturnType = method.getReturnType().getCanonicalName();
    // 方法名
    String methodName = method.getName();
    // 方法內容
    String methodContent = generateMethodContent(method);
    // 方法參數
    String methodArgs = generateMethodArguments(method);
    // 方法異常
    String methodThrows = generateMethodThrows(method);
    return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent);
}

其餘比較簡單,咱們只看generateMethodContent緩存

AdaptiveClassCodeGenerator.generateMethodContent

主要是方法體的代碼拼接app

private String generateMethodContent(Method method) {
    Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
    StringBuilder code = new StringBuilder(512);
    // 不是Adaptive註解的,方法體內拋異常,詳情見拼接好的代碼
    if (adaptiveAnnotation == null) {
        return generateUnsupported(method);
    } else {
        // 獲取url的位置
        int urlTypeIndex = getUrlTypeIndex(method);

        // found parameter in URL type
        if (urlTypeIndex != -1) {
            // Null Point check
            // 拼接判斷參數是否爲空,參考拼接好的refer方法
            code.append(generateUrlNullCheck(urlTypeIndex));
        } else {
            // did not find parameter in URL type
            // 不存在URL,方法體中判斷是否有某個參數的某個方法有URL返回,
            // 有的話,則返回的字符串參考拼接好的代碼,沒有的話,雷同上面拋異常的拼接字符串,參考拼接好的export方法
            // 這個方法,必須是方法名以 get 開頭,或方法名大於3個字符,方法的訪問權限爲 public,
            // 非靜態方法,方法參數數量爲0,方法返回值類型爲 URL,不符合拋異常
            code.append(generateUrlAssignmentIndirectly(method));
        }
        // 若是沒有值,獲取類名的小寫
        String[] value = getMethodAdaptiveValue(adaptiveAnnotation);
        // 是否有Invocation 類型的參數
        boolean hasInvocation = hasInvocationArgument(method);
        // 有的話,則拼接判斷是否爲空的異常代碼
        code.append(generateInvocationArgumentNullCheck(method));
        // String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        code.append(generateExtNameAssignment(value, hasInvocation));
        // check extName == null?
        // 拼接extName == null這行
        code.append(generateExtNameNullCheck(value));
        // 拼接getExtension這行
        code.append(generateExtensionAssignment());

        // return statement
        //  拼接返回值的最後一行
        code.append(generateReturnAndInvocation(method));
    }

    return code.toString();
}
相關文章
相關標籤/搜索