一文彻底理解Dubbo SPI 自适应(Adaptive)拓展原理

有关Dubbo SPI的源码分析请参考前篇博文:一篇短文就能搞定Dubbo SPI 源码及示例,本文介绍SPI自适应扩展相关实例及源码(源码版本:2.7.7)。

1. Dubbo SPI 自适应拓展简介

在 Dubbo 中,很多拓展都是通过 SPI 机制进行加载的,比如 Protocol、Cluster、LoadBalance 等。有时,有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。Dubbo 通过自适应拓展机制很好的解决了拓展加载及拓展方法调用的矛盾。自适应拓展机制的实现逻辑大致如:首先 Dubbo 会为拓展接口生成具有代理功能的代码。然后通过 javassist 或 jdk 编译这段代码,得到 Class 类。最后再通过反射创建代理类,整个过程比较复杂。

1.1 Dubbo Adaptive 注解

Adaptive 可注解在类或方法上。当 Adaptive 注解在类上时,Dubbo 不会为该类生成代理类。注解在方法(接口方法)上时,Dubbo 则会为该方法生成代理逻辑。Adaptive 注解在类上的情况很少,在 Dubbo 中,仅有两个类被 Adaptive 注解了,分别是 AdaptiveCompiler 和 AdaptiveExtensionFactory。此种情况,表示拓展的加载逻辑由人工编码完成。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
public @interface Adaptive {
	/**
	 * 
	 * 确定要注入的目标扩展。目标扩展名的名称由传递的参数URL中获取。 
	 * 如果在URL中找不到指定的参数,则将使用默认扩展名(即在SPI注解中设置的)依赖注入。
	 * 如果注解中参数也没有指定,则根据接口名称生成名称(如TestInvoker->test.invoker)
	 */
	String[] value() default {};

}

2. Dubbo SPI 自适应拓展源码

  1. 根据Dubbo SPI源码分析可知,实例化ExtensionLoader的时候,通过SPI获取了ExtensionFactory的拓展并使用getAdaptiveExtension方法获取了对应的自适应拓展,赋值给ExtensionLoader的属性,因此我们首先看下这个方法的源码.
    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;
    }

1.1. createAdaptiveExtension方法的源码

    private T createAdaptiveExtension() {
        try {
        	//1.getAdaptiveExtensionClass获取自适应拓展类
        	//2.反射newInstance创建实例
        	//3.使用setter依赖注入扩展injectExtension(主要是手工编码实现的拓展需要注入依赖)
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }

1.2. getAdaptiveExtensionClass和createAdaptiveExtensionClass方法源码

    private Class<?> getAdaptiveExtensionClass() {
    	//通过SPI获取所有扩展类(具体逻辑请参考文首提到的SPI源码博客)
        getExtensionClasses();
        //检查自适应拓展类缓存是否存在,存在直接返回
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        //创建自适应拓展实例
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

    private Class<?> createAdaptiveExtensionClass() {
    	//生成自适应拓展类代码,生成逻辑下一步介绍
        String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
        //优先使用线程上下文类加载器,不存在使用ExtensionLoader类的加载器
        ClassLoader classLoader = findClassLoader();
        //通过SPI获取Compiler的ExtensionLoader实例,再获取他的自适应拓展
        org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        //调用Compiler编译生成的拓展类代码,Dubbo 默认使用 javassist 作为编译器
        return compiler.compile(code, classLoader);
    }

  1. 接着分析上一步提到的AdaptiveClassCodeGenerator的generate方法,查看生成拓展代理类的源码。
 /**
     * 判断你是否存在Adaptive注解的方法
     */
    private boolean hasAdaptiveMethod() {
        return Arrays.stream(type.getMethods()).anyMatch(m -> m.isAnnotationPresent(Adaptive.class));
    }

    /**
     * generate and return class code
     */
    public String generate() {
        // 没用使用Adaptive注解的方法则直接抛出异常
        if (!hasAdaptiveMethod()) {
            throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
        }

        StringBuilder code = new StringBuilder();
        //生成包信息的代码(和当前拓展type(接口)同一个包)
        code.append(generatePackageInfo());
        //生成import信息(导入ExtensionLoader类)
        code.append(generateImports());
        //生成类名和实现接口(public class type$Adaptive implements type)
        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();
    }
/**
     * 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);
    }
  1. generateMethodContent生成方法主体内容,根据有无Adaptive注解分两种逻辑
 /**
     * generate method content
     */
    private String generateMethodContent(Method method) {
    	//获取方法上的Adaptive注解
        Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
        StringBuilder code = new StringBuilder(512);
        if (adaptiveAnnotation == null) {
        	//没有Adaptive注解的方法实现类直接抛出UnsupportedOperationException异常
            return generateUnsupported(method);
        } else {//有Adaptive注解生逻辑
        	//获取方法URL参数对应方法参数中的下标
            int urlTypeIndex = getUrlTypeIndex(method);

            if (urlTypeIndex != -1) {
                //方法中存在URL参数,生成判空检查的代码,如果URL传空抛出IllegalArgumentException异常
                code.append(generateUrlNullCheck(urlTypeIndex));
            } else {
                // 如果没有URL参数,则遍历所有参数,找到存在返回URL的参数,生成判空代码
                code.append(generateUrlAssignmentIndirectly(method));
            }
            // 获取Adaptive的属性值
            String[] value = getMethodAdaptiveValue(adaptiveAnnotation);
            // 是否存在org.apache.dubbo.rpc.Invocation类型参数
            boolean hasInvocation = hasInvocationArgument(method);
            // 生成Invocation参数判空逻辑
            code.append(generateInvocationArgumentNullCheck(method));
            //生成扩展名extName代码
            code.append(generateExtNameAssignment(value, hasInvocation));
            // 生成extName判空代码,空抛出IllegalStateException异常
            code.append(generateExtNameNullCheck(value));
            // 生成通过getExtensionLoader获取扩展代码
            code.append(generateExtensionAssignment());

            // 生成返回代码
            code.append(generateReturnAndInvocation(method));
        }

        return code.toString();
    }

  1. 上面生成代码中,还需要提一下的是没有URL参数时调用的generateUrlAssignmentIndirectly方法
 private String generateUrlAssignmentIndirectly(Method method) {
    	//获取所有方法参数类型
        Class<?>[] pts = method.getParameterTypes();

        Map<String, Integer> getterReturnUrl = new HashMap<>();
        // 遍历所有方法类型,找到返回 URL的方法(如getUrl)
        for (int i = 0; i < pts.length; ++i) {
            for (Method m : pts[i].getMethods()) {
                String name = m.getName();
                // 方法名以 get 开头,或方法名大于3个字符
                if ((name.startsWith("get") || name.length() > 3)
                		// 方法的访问权限为 public
                        && Modifier.isPublic(m.getModifiers())
                        // 非静态方法
                        && !Modifier.isStatic(m.getModifiers())
                        // 方法参数数量为0
                        && m.getParameterTypes().length == 0
                        // 方法返回值类型为 URL
                        && m.getReturnType() == URL.class) {
                	//保存方法名称和下标映射关系
                    getterReturnUrl.put(name, i);
                }
            }
        }
        // 如果没有找到抛出异常IllegalStateException
        if (getterReturnUrl.size() <= 0) {
            // 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());
        }
        
        Integer index = getterReturnUrl.get("getUrl");
        if (index != null) {
        	//存在getUrl,则直接生成getUrl方法判空检查代码
            return generateGetUrlNullCheck(index, pts[index], "getUrl");
        } else {
        	//没有getUrl方法,则为所有能获取URL的方法生成判空检查代码
            Map.Entry<String, Integer> entry = getterReturnUrl.entrySet().iterator().next();
            return generateGetUrlNullCheck(entry.getValue(), pts[entry.getValue()], entry.getKey());
        }
    }

到这里SPI自适应拓展的核心源码就分结束了,有一些小的细节可以自行查看dubbo源码中的common模块:https://github.com/qqxhb/dubbo/tree/master/dubbo-common
下篇相关博客:深入源码理解Dubbo负载均衡算法原理

发布了131 篇原创文章 · 获赞 7 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_43792385/article/details/105316283