文档章节

Dubbo-自适应拓展机制

rock-man
 rock-man
发布于 10/18 18:41
字数 4135
阅读 16
收藏 0

背景

    在 Dubbo 中,很多拓展都是通过 SPI 机制进行加载的,比如 Protocol、Cluster、LoadBalance 等,这些都是Dubbo的基础组件。这些基础组件的拓展不是在系统框架启动阶段被加载,而是拓展方法被调用时,根据运行时参数(URL)进行动态加载的。自适应拓展机制是建立在SPI基础之上的,自适应拓展类的核心实现:在拓展接口的方法被调用时,通过SPI加载具体的拓展实现类,并调用拓展对象的同名方法。可以看到自适应拓展是建立在SPI基础上的实现的功能,自适应拓展加上SPI实现Dubbo可拓展和解耦,这是Dubbo非常重要机制。

    自适应拓展类的过程是:选根据接口方法上的"@Adaptive" 注解生成自适应代码,然后通过Javassist编译创建自适应拓展类代码(使用系统内置特殊的自适应拓展类),再通过反射创建自适应拓展类的实例,并且保存至缓存。需要注意系统内置特殊的自适应拓展类是,分别是 AdaptiveCompiler 和 AdaptiveExtensionFactory,因为这两个类的类被 Adaptive 注解了(是类被注解,而不是方法)。Dubbo这样设计的原因是:在产生自适应拓展类代码时,为了避免死循环产生自适应拓展类代码,这一点很重要,特别是新手很容易搞混淆。

代码分析

创建自适应拓展类实例-逻辑图

    这里以"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实例。

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直接返回并且成为自适应拓展类,这是特殊的例子。

    在获取自适应拓展类实例,先是从缓存获取,如果缓存没有命中,再创建。因为创建一个自适应拓展类实例太耗资源和时间了,所以这些对象都是以单例方式运行。

@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,在注入依赖之前先获取自适应类,并创建实例。

@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。

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 等。

© 著作权归作者所有

rock-man
粉丝 17
博文 8
码字总数 22813
作品 0
深圳
私信 提问
聊聊Dubbo - Dubbo可扩展机制源码解析

摘要: 在Dubbo可扩展机制实战中,我们了解了Dubbo扩展机制的一些概念,初探了Dubbo中LoadBalance的实现,并自己实现了一个LoadBalance。是不是觉得Dubbo的扩展机制很不错呀,接下来,我们就...

阿里云云栖社区
2018/06/05
1K
0
聊聊Dubbo - Dubbo可扩展机制实战

1. Dubbo的扩展机制 在Dubbo的官网上,Dubbo描述自己是一个高性能的RPC框架。今天我想聊聊Dubbo的另一个很棒的特性, 就是它的可扩展性。 如同罗马不是一天建成的,任何系统都一定是从小系统不...

中间件小哥
2018/05/29
0
0
聊聊Dubbo - Dubbo可扩展机制实战

摘要: 在Dubbo的官网上,Dubbo描述自己是一个高性能的RPC框架。今天我想聊聊Dubbo的另一个很棒的特性, 就是它的可扩展性。 1. Dubbo的扩展机制 在Dubbo的官网上,Dubbo描述自己是一个高性能...

阿里云云栖社区
2018/06/04
156
1
探究Dubbo的ExtensionLoader

一定要对比JDK的SPI机制,才能更好的理解Dubbo的SPI实现! 扩展点:即接口扩展:扩展点的实现,即接口的实现类 获取扩展点的ServiceLoader实例 通过扩展点的ServiceLoader实例,完成扩展的方...

向码而生
2018/09/03
167
0
Dubbo源码分析-SPI的应用

SPI简介 SPI是Service Provider Interface的缩写,即服务提供接口(翻译出来好绕口,还是不翻译的好),实质上是接口,作用是对外提供服务。 SPI是Java的一种插件机制,可以不用修改源代码实现新...

农码人生
2018/07/24
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Vue.js学习笔记2 - better-scroll滚动条

better-scroll滚动条 使用作者自制的better-scroll库,实现内容的滚动。 先在package.json加上依赖: "better-scroll": "^0.1.7" 接着再npm install安装依赖。 import BScroll from 'better-......

swanf
今天
7
0
设计模式之适配器模式

定义 将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工 作。 UML类图 适配器分为两种,类适配器与对象适配器。 类适配器的UML图...

陈年之后是青葱
今天
8
0
教你玩转Linux—磁盘管理

导读 Linux磁盘管理好坏直接关系到整个系统的性能问题,Linux磁盘管理常用三个命令为df、du和fdisk。 df df命令参数功能:检查文件系统的磁盘空间占用情况。可以利用该命令来获取硬盘被占用了...

问题终结者
今天
13
0
KMP

字符串匹配算法 针对被匹配字段生产一个部分匹配表 A B C D A B D 0 0 0 0 1 2 0 部分匹配表 熟悉前缀与后缀的概念 ,“部分匹配表” 的生产就是根据前缀、后缀的最苍的共有元素的长度 前缀:...

鬼才王
昨天
6
0
快速搭建Jenkins集群

关于Jenkins集群 在Jenkins上同时执行多个任务时,单机性能可能达到瓶颈,使用Jenkins集群可以有效的解决此问题,让多台机器同时处理这些任务可以将压力分散,对单机版Jenkins的单点故障的隐...

程序员欣宸
昨天
13
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部