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

2023年 8月 18日 47.1k 0

有关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 自适应拓展源码

  • 根据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);
        }
    
  • 接着分析上一步提到的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);
        }
    
  • 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();
        }
    
  • 上面生成代码中,还需要提一下的是没有URL参数时调用的generateUrlAssignmentIndirectly方法
  •  private String generateUrlAssignmentIndirectly(Method method) {
    //获取所有方法参数类型
    Class[] pts = method.getParameterTypes();

    Map getterReturnUrl = new HashMap();
    // 遍历所有方法类型,找到返回 URL的方法(如getUrl)
    for (int i = 0; i 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()

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论