• dubbo SPI源码分析


    dubbo版本2.7.8

    先说用法:

    1.在扩展点接口上加@SPI注解,value值为默认扩展类name

    2.要么扩展点接口中至少有一个方法被标记为@Adaptive,要么必须有一个扩展类被标记为@Adaptive(META-INF/dubbo/internal/目录下有且只能有一个扩展类被标记为@Adaptive)

    3.如果有且仅有一个扩展类标记@Adaptive,那么这个类就是AdaptiveExtension,如果有多个的话,按照下面的顺序覆盖,其中,META-INF/dubbo/internal/目录下有且只能有一个扩展类被标记为@Adaptive

    META-INF/dubbo/internal/

    META-INF/dubbo/

    META-INF/services/

    否则就通过ExtensionLoader#createAdaptiveExtensionClass方法动态创建AdaptiveExtension实例

    4.org.apache.dubbo.common.extension.ExtensionLoader#getAdaptiveExtension方法返回的具体扩展实现类的顺序为:

    a.有@Adaptive标注的扩展类就直接返回该类,顾名思义就是自己实现的适配器类,通过这个类返回具体的扩展实例(@Adaptive规则见上面所述)

    b.如果没有@Adaptive标注的扩展类,就动态拼接字符串然后动态编译成class返回。

    c.动态编译class的逻辑中,重点在构建方法内容这里,适配具体扩展实例主要依赖入参中的org.apache.dubbo.common.URL,核心目标是按照既定规则适配出一个具体的extName,也就是明确的扩展实例name,大体如下:

      i.识别@Adaptive标注的方法,并从入参中查找org.apache.dubbo.common.URL,或者入参class中是否有getUrl方法;

      ii如果是Protocol,就通过url.getProtocol()确定extName,如果不是,就通过url.getParameter("xxx")获取extName,这里的xxx来自SPI接口中方法标注的@Adaptive的value,如果未配置value就取值接口名字的转换值,例如接口名字为ServiceInstanceSelector,那这里的xxx就为service.instance.selector,如果@SPI有value值即为默认extName,通过url.getParameter("xxx", "@SPI中的value")获取url的parameter。

    当然,入参中如果有org.apache.dubbo.rpc.Invocation,这种情况就是通过url.getMethodParameter获取extName。

    @Adaptive如果配置多个value,优先级就是从前往后依次递减,@Adaptive({"a", "b"})生成的代码形如:url.getParameter("a", url.getParameter("b", "@SPI中的value"))

    上述基本就是约定的使用方法,如果对SPI机制比较了解或者自己扩展过的话就直接看下面的源码分析。

    我们从使用方法上入手:

    private static final Protocol PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

    看下getExtensionLoader方法:

    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
            if (type == null) {
                throw new IllegalArgumentException("Extension type == null");
            }
            if (!type.isInterface()) {
                throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
            }
    // 检查接口是否标注了SPI注解,如果没有标注就抛出异常
    if (!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type (" + type + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!"); } 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; }
    private ExtensionLoader(Class<?> type) {
            this.type = type;
         // objectFactory在创建完扩展点实例后setter注入属性值时的方法injectExtension中用到 objectFactory
    = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }

     下面我们看下getAdaptiveExtension方法:

    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 Class<?> getAdaptiveExtensionClass() {
    // 加载所有通过SPI方式扩展的class并放入cachedClasses getExtensionClasses();
         // 如果已经有缓存的适配器类就直接返回,这里注意,并非下面createAdaptiveExtensionClass一处设置该值
    // 上面getExtensionClasses方法在加载class的时候调用ExtensionLoader#loadClass方法判断如果标记Adaptive.class注解
    // 就调用ExtensionLoader#cacheAdaptiveClass给cachedAdaptiveClass赋值,所以就直接返回了,不去动态创建adaptive类
    if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } return cachedAdaptiveClass = createAdaptiveExtensionClass(); }

    下面看下createAdaptiveExtensionClass如何动态创建adaptive类的

    private Class<?> createAdaptiveExtensionClass() {
    // 构建class字符串用于下面通过编译器生产class对象,这里的cachedDefaultName为SPI注解的value值,可为
    // 空,type为加了SPI注解的接口类 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); }

    看下org.apache.dubbo.common.extension.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!"); } // 构建package,import,class声明 StringBuilder code = new StringBuilder(); code.append(generatePackageInfo()); 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(); }
    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); }
    private String generateMethodContent(Method method) {
            Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
            StringBuilder code = new StringBuilder(512);
    // 如果方法没有adaptive注解直接生成一个抛出UnsupportedOperationException异常的代码
    if (adaptiveAnnotation == null) { return generateUnsupported(method); } else {
    // 查找入参中是否有URL类
    int urlTypeIndex = getUrlTypeIndex(method); // found parameter in URL type
    // 存在URL类型参数,直接取值该参数url = arg
    if (urlTypeIndex != -1) { // Null Point check code.append(generateUrlNullCheck(urlTypeIndex)); } else { // did not find parameter in URL type
    // 未找到URL,遍历所有入参,查找是否存在返回值类型是URL的method,如果有,就生成这样的代码,通过该方法获取URL
    // 如果没有找到这样的method就直接抛出异常
    code.append(generateUrlAssignmentIndirectly(method)); } // 获取adaptive注解value,如果没有配置就用接口类名称以.符号分隔驼峰格式形成的字符串,该值用于获取从URL获取extName String[] value = getMethodAdaptiveValue(adaptiveAnnotation); // 入参中是否有org.apache.dubbo.rpc.Invocation类,如果有的话就用url.getMethodParameter获取extName,如果没有的话,
    // 就通过url.getParameter获取extName
    boolean hasInvocation = hasInvocationArgument(method); code.append(generateInvocationArgumentNullCheck(method)); // 这是重点,获取extName 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 generateExtNameAssignment(String[] value, boolean hasInvocation) {
            // TODO: refactor it
            String getNameCode = null;
            for (int i = value.length - 1; i >= 0; --i) {
                if (i == value.length - 1) {
                    if (null != defaultExtName) {
                        if (!"protocol".equals(value[i])) {
                            if (hasInvocation) {
    // 非protocol并且入参中有org.apache.dubbo.rpc.Invocation就通过getMethodParameter获取,
    // 这里注意,默认值为SPI注解的value getNameCode
    = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); } else {
    // 根据adaptive配置的value值循环拼接取值,如果多个就会形成类似这样的
    // 获取extName代码:url.getParameter("a", url.getParameter("b", "@SPI中的value")) getNameCode
    = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName); } } else { 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 { getNameCode = String.format("url.getParameter(\"%s\")", value[i]); } } else { getNameCode = "url.getProtocol()"; } } } else { if (!"protocol".equals(value[i])) { if (hasInvocation) { getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); } else { getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode); } } else { getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode); } } } return String.format(CODE_EXT_NAME_ASSIGNMENT, getNameCode); }

    获取extName后,通过具体扩展类调用method返回。

    org.apache.dubbo.common.extension.AdaptiveClassCodeGenerator#generateExtensionAssignment

     至此,主体逻辑就讲完了,下面贴出Protocol和ProxyFactory对应的adaptive生产结果:

    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);
    }
    public java.util.List getServers()  {
    throw new UnsupportedOperationException("The method public default java.util.List org.apache.dubbo.rpc.Protocol.getServers() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
    }
    }
    package org.apache.dubbo.rpc;
    import org.apache.dubbo.common.extension.ExtensionLoader;
    public class ProxyFactory$Adaptive implements org.apache.dubbo.rpc.ProxyFactory {
    public java.lang.Object getProxy(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.getParameter("proxy", "javassist");
    if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
    org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
    return extension.getProxy(arg0);
    }
    public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0, boolean arg1) 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.getParameter("proxy", "javassist");
    if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
    org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
    return extension.getProxy(arg0, arg1);
    }
    public org.apache.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, org.apache.dubbo.common.URL arg2) throws org.apache.dubbo.rpc.RpcException {
    if (arg2 == null) throw new IllegalArgumentException("url == null");
    org.apache.dubbo.common.URL url = arg2;
    String extName = url.getParameter("proxy", "javassist");
    if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
    org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
    return extension.getInvoker(arg0, arg1, arg2);
    }
    }

     adaptive类创建完了,还会调用org.apache.dubbo.common.extension.ExtensionLoader#injectExtension注入属性值,该方法在手动调用getExtension的时候也会注入,下面就具体看下这个方法:

    private T injectExtension(T instance) {
            // 构造方法赋值,实例为ExtensionFactory.class的adaptive类
            if (objectFactory == null) {
                return instance;
            }
    
            try {
                for (Method method : instance.getClass().getMethods()) {
    // 通过setter方法注入,不是setter方法直接跳过
    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);
    // 通过objectFactory的具体扩展类获取set的实例对象
    // 查看ExtensionFactory.class接口类发现SPI没有默认值且getExtension方法没有加adaptive注解,
    // 通过上面的分析可知,必定存在一个SPI扩展类标注了adaptive注解,此类就是自动适配器类,翻看所有实现类,
    // 定位为AdaptiveExtensionFactory类,getExtension方法内容如下图所示,由代码可知factories为
    // ExtensionFactory.class接口的所有扩展实现类,目前版本有两个扩展实现SpiExtensionFactory和SpringExtensionFactory
    // 顾名思义,SpringExtensionFactory是从spring容器中获取实例,SpiExtensionFactory则通过SPI机制扩展方式获取property名字
    // 对应的扩展实现名称,具体选择哪个?如果入参的类型是个接口并且被SPI注解标注就用SPI方式获取实例注入,否则就是spring的方式
    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;
        }

     

     

  • 相关阅读:
    [dll注入实现IAT勾取] 计算器显示中文
    [记事本API Hook] 调试器 勾取 WriteFile()Api
    [DLL注入的方法]进程创建期修改PE输入表法
    [DLL注入的方法]静态修改PE输入表法
    [1]编程实现加载驱动
    [保护模式]测试一致代码段
    [保护模式]联系1 三环访问高2G
    15.[保护模式]TSS任务门
    ERP中HR模块的操作与设计--开源软件诞生26
    ERP的主数据的操作与设计--开源软件诞生25
  • 原文地址:https://www.cnblogs.com/reboot30/p/15043470.html
Copyright © 2020-2023  润新知