dubbo的自适应拓展

162 阅读4分钟

原理概览

在dubbo中很多拓展都是通过SPI机制进行加载的,比如Protocol、Cluster、LoaderBalance等。同时,有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载,在dubbo中这一过程就是通过自适应拓展来完成的。 其实这个过程大概就是三个过程:

  1. 从URL中获取需要模块的名称
  2. 通过SPI加载该模块的实现类
  3. 调用目标方法

源码分析

1. Adaptive的注解

Adaptive注解可以注解在方法也可以注解在类上。Adaptive注解在方法上时会给该方法生成代理逻辑,并且一般不用做于注解在类上。

1.1 获取自适应拓展

createAdaptiveExtension方法中包含了三个逻辑:

  1. 调用getAdaptiveExtensionClass()获取自适应拓展类
  2. 通过反射进行实例化
  3. 调用injectExtension方法向自适应实例中注入依赖
    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;
    }
    
        private T createAdaptiveExtension() {
        try {
            // 1. 首先获自适应拓展类
            // 2. 实例话
            // 3. 通过injectExtension方法对其进行属性注入
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }
    
   
    private T injectExtension(T instance) { // 这里的instance其实也是从ExtensionLoader<T>中传入的

        if (objectFactory == null) {
            return instance;
        }

        try {
            for (Method method : instance.getClass().getMethods()) {
                if (!isSetter(method)) {
                    continue;
                }
                /**
                 * Check {@link DisableInject} to see if we need auto injection for this property
                 */
                if (method.getAnnotation(DisableInject.class) != null) {
                    continue;
                }
                Class<?> pt = method.getParameterTypes()[0];
                if (ReflectUtils.isPrimitives(pt)) {
                    continue;
                }

                try {
                    String property = getSetterProperty(method);
                    Object object = objectFactory.getExtension(pt, property);
                    if (object != null) {
                        method.invoke(instance, object);
                    }
                } catch (Exception e) {
                    logger.error("Failed to inject via method " + method.getName()
                            + " of interface " + type.getName() + ": " + e.getMessage(), e);
                }

            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return instance;
    }

在getAdaptiveExtensionClass()方法中,虽然方法名称与一开始的提到的自适应拓展的方法的入口相类似,但是内容是完全不同的。方法的具体实现如下:

    private Class<?> getAdaptiveExtensionClass() {

        // 通过SPI获取所有的拓展类
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        return cachedAdaptiveClass = createAdaptiveExtensionClass(); // 创建拓展类
    }
    
   private Class<?> createAdaptiveExtensionClass() {
        // gengerate 是产生代码的
        // findClassLoader获取类加载器
        // 进行编译

        String code = new AdaptiveClassCodeGenerator(type/** 对象接口 */, cachedDefaultName).generate();
        ClassLoader classLoader = findClassLoader();
        org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        return compiler.compile(code, classLoader);
    }

下面看下自适应拓展累代码的生成:

    /**
     * test if given type has at least one method annotated with <code>Adaptive</code>
     */
    private boolean hasAdaptiveMethod() {
        return Arrays.stream(type.getMethods()).anyMatch(m -> m.isAnnotationPresent(Adaptive.class));
    }

    /**
     * generate and return class code
     */
    public String generate() {
        // no need to generate adaptive class since there's no adaptive method found.
        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()); // 包申明
        code.append(generateImports()); // import构造
        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();
    }

可以看到hasAdaptiveMethod()方法会首先对type【createAdaptiveExtensionClass()方法中传入的,其实上述方法都是在ExtensionLoader类中进行定义的,而type是该类中的一个属性,所以看起来没有明确的输入输出】进行反射,判断是否至少在一个方法上含有adaptive注解,否则抛出异常。generate()就是具体的生成代码的函数。其中相对来说比较复杂是generateMethod()方法。

    /**
     * generate method declaration
     */
    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);
    }
    
        /**
     * generate method content
     */
    private String generateMethodContent(Method method) {
        Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
        StringBuilder code = new StringBuilder(512);
        if (adaptiveAnnotation == null) {
            return generateUnsupported(method); // 如果方法上没有Adaptive注解就抛出unsupported exception异常
        } else {
            int urlTypeIndex = getUrlTypeIndex(method); // 参数中含有URL类型,并返回URL在参数列表中的位置

            // found parameter in URL type
            if (urlTypeIndex != -1) {
                // Null Point check
                code.append(generateUrlNullCheck(urlTypeIndex)); // 如果有URL参数,则进行判空
            } else {
                // did not find parameter in URL type
                code.append(generateUrlAssignmentIndirectly(method)); // 不含有URL参数,
            }

            String[] value = getMethodAdaptiveValue(adaptiveAnnotation);

            boolean hasInvocation = hasInvocationArgument(method);

            code.append(generateInvocationArgumentNullCheck(method));

            code.append(generateExtNameAssignment(value, hasInvocation)); // 获取拓展名
            // check extName == null?
            code.append(generateExtNameNullCheck(value));

            code.append(generateExtensionAssignment());

            // return statement
            code.append(generateReturnAndInvocation(method));
        }

        return code.toString();
    }

接下来就是生成拓展加载与目标方法调用逻辑

    private String generateReturnAndInvocation(Method method) {
        String returnStatement = method.getReturnType().equals(void.class) ? "" : "return ";

        String args = IntStream.range(0, method.getParameters().length)
                .mapToObj(i -> String.format(CODE_EXTENSION_METHOD_INVOKE_ARGUMENT, i))
                .collect(Collectors.joining(", "));

        return returnStatement + String.format("extension.%s(%s);\n", method.getName(), args);
    }

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

其他的就是方法的补全了,不再进行赘述。至此为止,dubbo的自适应拓展就算完成了。