前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >二、SPI 自适应拓展

二、SPI 自适应拓展

作者头像
JathonKatu
发布2022-12-02 19:38:35
1910
发布2022-12-02 19:38:35
举报
文章被收录于专栏:JathonKatuJathonKatu

本文是自己阅读dubbo源码的时候的见解,因为sb微信觉得我跟另一个人的重复率太高(他是纯copy官方文档),所以不能搞原创,,自闭gg,要不是公众号自己复习方便,真的是不用了。。。

在 Dubbo 中,很多拓展都是通过 SPI 机制进行加载的,比如 Protocol、Cluster、LoadBalance 等。

有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。这听起来有些矛盾。拓展未被加载,那么拓展方法就无法被调用(静态方法除外)。拓展方法未被调用,拓展就无法被加载。对于这个矛盾的问题,Dubbo 通过自适应拓展机制很好的解决了。自适应拓展机制的实现逻辑比较复杂,首先 Dubbo 会为拓展接口生成具有代理功能的代码。然后通过 javassist 或 jdk 编译这段代码,得到 Class 类。最后再通过反射创建代理类。

在对自适应拓展生成过程进行深入分析之前,我们先来看一下与自适应拓展息息相关的一个注解,即 Adaptive 注解。该注解的定义如下:

代码语言:javascript
复制
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
    String[] value() default {};
}

Adaptive 可注解在类或方法上。当 Adaptive 注解在类上时,Dubbo 不会为该类生成代理类。注解在方法(接口方法)上时,Dubbo 则会为该方法生成代理逻辑。Adaptive 注解在类上的情况很少,在 Dubbo 中,仅有两个类被 Adaptive 注解了,分别是 AdaptiveCompiler 和 AdaptiveExtensionFactory。此种情况,表示拓展的加载逻辑由人工编码完成。更多时候,Adaptive 是注解在接口方法上的,表示拓展的加载逻辑需由框架自动生成。Adaptive 注解的地方不同,相应的处理逻辑也是不同的。注解在类上时,处理逻辑比较简单,本文就不分析了。注解在接口方法上时,处理逻辑较为复杂.

getAdaptiveExtension 方法是获取自适应拓展的入口方法,因此下面我们从这个方法进行分析。相关代码如下:

getAdaptiveExtension 方法首先会检查缓存,缓存未命中,则调用 createAdaptiveExtension 方法创建自适应拓展。

代码语言:javascript
复制
public T getAdaptiveExtension() {
    // 在缓存中获取自适应扩展
    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("fail to create adaptive instance: " + t.toString(), t);
                    }
                }
            }
        } else {
            throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
        }
    }

    return (T) instance;
}

下面,我们看一下 createAdaptiveExtension 方法的代码。

createAdaptiveExtension 方法的代码比较少,但却包含了三个逻辑,分别如下:

  1. 调用 getAdaptiveExtensionClass 方法获取自适应拓展 Class 对象
  2. 通过反射进行实例化
  3. 调用 injectExtension 方法向拓展实例中注入依赖

前两个逻辑比较好理解,第三个逻辑用于向自适应拓展对象中注入依赖。这个逻辑看似多余,但有存在的必要,这里简单说明一下。前面说过,Dubbo 中有两种类型的自适应拓展,一种是手工编码的,一种是自动生成的。手工编码的自适应拓展中可能存在着一些依赖,而自动生成的 Adaptive 拓展则不会依赖其他类。这里调用 injectExtension 方法的目的是为手工编码的自适应拓展注入依赖,这一点需要注意一下。

代码语言:javascript
复制
private T createAdaptiveExtension() {
    try {
        // 获取自适应拓展类,并通过反射实例化
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        throw new IllegalStateException("Can not create adaptive extension ...");
    }
}

getAdaptiveExtensionClass 方法同样包含了三个逻辑,如下:

  1. 调用 getExtensionClasses 获取所有的拓展类
  2. 检查缓存,若缓存不为空,则返回缓存
  3. 若缓存为空,则调用 createAdaptiveExtensionClass 创建自适应拓展类

这三个逻辑看起来平淡无奇,似乎没有多讲的必要。但是这些平淡无奇的代码中隐藏了着一些细节,需要说明一下。首先从第一个逻辑说起,getExtensionClasses 这个方法用于获取某个接口的所有实现类。比如该方法可以获取 Protocol 接口的 DubboProtocol、HttpProtocol、InjvmProtocol 等实现类。在获取实现类的过程中,如果某个实现类被 Adaptive 注解修饰了,那么该类就会被赋值给 cachedAdaptiveClass 变量。此时,上面步骤中的第二步条件成立(缓存不为空),直接返回 cachedAdaptiveClass 即可。如果所有的实现类均未被 Adaptive 注解修饰,那么执行第三步逻辑,创建自适应拓展类。

代码语言:javascript
复制
private Class<?> getAdaptiveExtensionClass() {
    // 加载扩展类
    getExtensionClasses();
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    // 手动编写一个Service$Adaptive类并且保存起来
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

createAdaptiveExtensionClass 方法用于生成自适应拓展类,该方法首先会生成自适应拓展类的源码,然后通过 Compiler 实例(Dubbo 默认使用 javassist 作为编译器)编译源码,得到代理类 Class 实例。(具体看笔记 二(扩展)@Adaptive)

自适应拓展类代码生成

createAdaptiveExtensionClassCode的代码比较多,接下来的解析需要耐心去看

代码语言:javascript
复制
private String createAdaptiveExtensionClassCode() {
    StringBuilder codeBuilder = new StringBuilder();
    // 反射获取到所有的方法
    Method[] methods = type.getMethods();
    // ------ Adaptive 注解检测
    boolean hasAdaptiveAnnotation = false;
    // 遍历方法检查是否有Adaptive注解
    for (Method m : methods) {
        if (m.isAnnotationPresent(Adaptive.class)) {
            hasAdaptiveAnnotation = true;
            break;
        }
    }
    // 若不存在Adaptive注解,则说明不应该生成Adaptive自适应类,报异常
    // no need to generate adaptive class since there's no adaptive method found.
    if (!hasAdaptiveAnnotation)
        throw new IllegalStateException("No adaptive method on extension " + type.getName() + ", refuse to create the adaptive class!");

    // ------ 首先会生成 package 语句,然后生成 import 语句,紧接着生成类名等代码。
    // 生成 package 代码:package + type 所在包
    codeBuilder.append("package ").append(type.getPackage().getName()).append(";");
    // 生成 import 代码:import + ExtensionLoader 全限定名
    codeBuilder.append("\nimport ").append(ExtensionLoader.class.getName()).append(";");
    // 生成类代码:public class + type简单名称 + $Adaptive + implements + type全限定名 + {
    codeBuilder.append("\npublic class ").append(type.getSimpleName()).append("$Adaptive").append(" implements ").append(type.getCanonicalName()).append(" {");


    for (Method method : methods) {
        Class<?> rt = method.getReturnType();
        Class<?>[] pts = method.getParameterTypes(); // 获取参数类型列表
        Class<?>[] ets = method.getExceptionTypes();

        Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
        StringBuilder code = new StringBuilder(512);
        // ------ 无 Adaptive 注解方法代码生成逻辑
        // 如果不被Adaptive注解修饰,则调用直接报不支持操作异常(类似于AbstractQueuedSynchronizer里面的tryAcquire方法),标识不让调用
        if (adaptiveAnnotation == null) {
            // 生成的代码格式如下:
            // throw new UnsupportedOperationException(
            //     "method " + 方法签名 + of interface + 全限定接口名 + is not adaptive method!”)
            code.append("throw new UnsupportedOperationException(\"method ")
                    .append(method.toString()).append(" of interface ")
                    .append(type.getName()).append(" is not adaptive method!\");");
        } else {
            // ------ 如果被@Adaptive修饰,说明生成的代理类中需要实现这个方法
            int urlTypeIndex = -1;
            // dubbo主要是基于URL获取参数去生成自适应类,所以先看一下方法的参数是否有URL类
            for (int i = 0; i < pts.length; ++i) {
                if (pts[i].equals(URL.class)) {
                    urlTypeIndex = i;
                    break;
                }
            }
            // ------ 获取 URL 数据
            // found parameter in URL type
            // 如果方法的参数中有URL类,则直接编写代码
            if (urlTypeIndex != -1) {
                // Null Point check
                // 生成URL判空代码
                // if (arg + urlTypeIndex == null) 
                //     throw new IllegalArgumentException("url == null");
                String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"url == null\");",
                        urlTypeIndex);
                code.append(s);
                
                // 为 URL 类型参数生成赋值代码,形如 URL url = arg1
                s = String.format("\n%s url = arg%d;", URL.class.getName(), urlTypeIndex);
                code.append(s);
            }
            // did not find parameter in URL type
            // 如果方法的参数中找不到url类,则找参数的字段中是否有URL类
            else {
                String attribMethod = null;

                // find URL getter method
                LBL_PTS:
                // 遍历方法的参数类型列表
                for (int i = 0; i < pts.length; ++i) {
                    // 获取某一类型参数的全部方法
                    Method[] ms = pts[i].getMethods();
                    // 1. 方法名以 get 开头,或方法名大于3个字符
                    // 2. 方法的访问权限为 public
                    // 3. 非静态方法
                    // 4. 方法参数数量为0
                    // 5. 方法返回值类型为 URL
                    for (Method m : ms) {
                        String name = m.getName();
                        // 反射找到getter方法,并且返回参数URL类的,标识URL类的参数位置及对应的方法名
                        if ((name.startsWith("get") || name.length() > 3)
                                && Modifier.isPublic(m.getModifiers())
                                && !Modifier.isStatic(m.getModifiers())
                                && m.getParameterTypes().length == 0
                                && m.getReturnType() == URL.class) {
                            urlTypeIndex = i;
                            attribMethod = name;
                            // 结束 for (int i = 0; i < pts.length; ++i) 循环
                            break LBL_PTS;
                        }
                    }
                }
                
                if (attribMethod == null) {
                    // 如果所有参数中均不包含可返回 URL 的 getter 方法,则抛出异常
                    throw new IllegalStateException("fail to create adaptive class for interface " + type.getName()
                            + ": not found url parameter or url attribute in parameters of method " + method.getName());
                }

                // Null point check
                // 为可返回 URL 的参数生成判空代码,格式如下:
                // if (arg + urlTypeIndex == null) 
                //     throw new IllegalArgumentException("参数全限定名 + argument == null");
                String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"%s argument == null\");",
                        urlTypeIndex, pts[urlTypeIndex].getName());
                code.append(s);
                
                // 为 getter 方法返回的 URL 生成判空代码,格式如下:
                // if (argN.getter方法名() == null) 
                //     throw new IllegalArgumentException(参数全限定名 + argument getUrl() == null);
                s = String.format("\nif (arg%d.%s() == null) throw new IllegalArgumentException(\"%s argument %s() == null\");",
                        urlTypeIndex, attribMethod, pts[urlTypeIndex].getName(), attribMethod);
                code.append(s);

            // 生成赋值语句,格式如下:
            // URL全限定名 url = argN.getter方法名(),比如 
            // com.alibaba.dubbo.common.URL url = invoker.getUrl();
                s = String.format("%s url = arg%d.%s();", URL.class.getName(), urlTypeIndex, attribMethod);
                code.append(s);
            }

            // ------获取 Adaptive 注解值
            String[] value = adaptiveAnnotation.value();
            // value is not set, use the value generated from class name as the key
            // value 为空数组
            if (value.length == 0) {
                // 获取类名,并将类名转换为字符数组
                char[] charArray = type.getSimpleName().toCharArray();
                StringBuilder sb = new StringBuilder(128);
                // 遍历字节数组
                for (int i = 0; i < charArray.length; i++) {
                    // 检测当前字符是否为大写字母
                    if (Character.isUpperCase(charArray[i])) {
                        if (i != 0) {
                            // 向 sb 中添加点号
                            sb.append(".");
                        }
                        // 将字符变为小写,并添加到 sb 中
                        sb.append(Character.toLowerCase(charArray[i]));
                    } else {
                        // 添加字符到 sb 中
                        sb.append(charArray[i]);
                    }
                }
                value = new String[]{sb.toString()};
            }


            // ------此段逻辑是检测方法列表中是否存在 Invocation 类型的参数,若存在,则为其生成判空代码和其他一些代码。
            
            boolean hasInvocation = false;
            // 遍历参数类型列表
            for (int i = 0; i < pts.length; ++i) {
                // 判断当前参数名称是否等于 com.alibaba.dubbo.rpc.Invocation
                if (pts[i].getName().equals("com.alibaba.dubbo.rpc.Invocation")) {
                    // Null Point check
                    // 为 Invocation 类型参数生成判空代码
                    String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"invocation == null\");", i);
                    code.append(s);
                    // 生成 getMethodName 方法调用代码,格式为:
                    //    String methodName = argN.getMethodName();
                    s = String.format("\nString methodName = arg%d.getMethodName();", i);
                    code.append(s);
                    // 设置 hasInvocation 为 true
                    hasInvocation = true;
                    break;
                }
            }

            // ------ 生成拓展名获取逻辑 本段逻辑用于根据 SPI 和 Adaptive 注解值生成“获取拓展名逻辑”,同时生成逻辑也受 Invocation 类型参数影响,综合因素导致本段逻辑相对复杂
            // 设置默认拓展名,cachedDefaultName 源于 SPI 注解值,默认情况下,
            // SPI 注解值为空串,此时 cachedDefaultName = null
            String defaultExtName = cachedDefaultName;
            String getNameCode = null;
            // 遍历 value,这里的 value 是 Adaptive 的注解值。
            // 此处循环目的是生成从 URL 中获取拓展名的代码,生成的代码会赋值给 getNameCode 变量。
            // 注意这个循环的遍历顺序是由后向前遍历的。
            for (int i = value.length - 1; i >= 0; --i) {
                // 当 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 赋值代码
            code.append("\nString extName = ").append(getNameCode).append(";");
            // check extName == null?
            // 生成 extName 判空代码
            String s = String.format("\nif(extName == null) " +
                            "throw new IllegalStateException(\"Fail to get extension(%s) name from url(\" + url.toString() + \") use keys(%s)\");",
                    type.getName(), Arrays.toString(value));
            code.append(s);
            
            // ------ 生成拓展加载与目标方法调用逻辑  根据拓展名加载拓展实例,并调用拓展实例的目标方法。
             // 生成拓展获取代码,格式如下:
            // type全限定名 extension = (type全限定名)ExtensionLoader全限定名
            //     .getExtensionLoader(type全限定名.class).getExtension(extName);
            // Tips: 格式化字符串中的 %<s 表示使用前一个转换符所描述的参数,即 type 全限定名
            s = String.format("\n%s extension = (%<s)%s.getExtensionLoader(%s.class).getExtension(extName);",
                    type.getName(), ExtensionLoader.class.getSimpleName(), type.getName());
            code.append(s);

            // 如果方法返回值类型非 void,则生成 return 语句。
            if (!rt.equals(void.class)) {
                code.append("\nreturn ");
            }

            // 生成目标方法调用逻辑,格式为:
            //     extension.方法名(arg0, arg2, ..., argN);
            s = String.format("extension.%s(", method.getName());
            code.append(s);
            for (int i = 0; i < pts.length; i++) {
                if (i != 0)
                    code.append(", ");
                code.append("arg").append(i);
            }
            code.append(");");
        }

        // ------ 生成完整的方法
        // public + 返回值全限定名 + 方法名 + (
        codeBuilder.append("\npublic ").append(rt.getCanonicalName()).append(" ").append(method.getName()).append("(");
        // 添加参数列表代码
        for (int i = 0; i < pts.length; i++) {
            if (i > 0) {
                codeBuilder.append(", ");
            }
            codeBuilder.append(pts[i].getCanonicalName());
            codeBuilder.append(" ");
            codeBuilder.append("arg").append(i);
        }
        codeBuilder.append(")");
        // 添加异常抛出代码
        if (ets.length > 0) {
            codeBuilder.append(" throws ");
            for (int i = 0; i < ets.length; i++) {
                if (i > 0) {
                    codeBuilder.append(", ");
                }
                codeBuilder.append(ets[i].getCanonicalName());
            }
        }
        codeBuilder.append(" {");
        codeBuilder.append(code.toString());
        codeBuilder.append("\n}");
    }
    // 类的最后一个花括号,public class xxxx {  对应的 } 
    codeBuilder.append("\n}");
    if (logger.isDebugEnabled()) {
        logger.debug(codeBuilder.toString());
    }
    return codeBuilder.toString();
}

生成的类文件的基本架构,用protocol举例

代码语言:javascript
复制
// ------ 首先会生成 package 语句,然后生成 import 语句,紧接着生成类名等代码。
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
    // 省略方法代码
}

抛异常以protocol为例:

代码语言:javascript
复制
// ------ 无 Adaptive 注解方法代码生成逻辑
throw new UnsupportedOperationException(
            "method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");

protocol接口需要实现的方法就两个:

代码语言:javascript
复制
// ------ 如果被@Adaptive修饰,说明生成的代理类中需要实现这个方法
Invoker refer(Class<T> arg0, URL arg1) throws RpcException;
Exporter export(Invoker<T> arg0) throws RpcException;

根据上面的代码,生成的protocol类的两个方法refer和export分别是:

代码语言:javascript
复制
refer:
if (arg1 == null) 
    throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg1;

export:
if (arg0 == null) 
    throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null) 
    throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
com.alibaba.dubbo.common.URL url = arg0.getUrl();

生成扩展名的逻辑:

代码语言:javascript
复制
// ------ 生成拓展名获取逻辑 本段逻辑用于根据 SPI 和 Adaptive 注解值生成“获取拓展名逻辑”,同时生成逻辑也受 Invocation 类型参数影响,综合因素导致本段逻辑相对复杂
// 可能会生成以下代码
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
String extName = url.getMethodParameter(methodName, "loadbalance", "random");
String extName = url.getParameter("client", url.getParameter("transporter", "netty"));

生成目标方法调用逻辑:

protocol的refer:

代码语言:javascript
复制
// ------ 生成拓展加载与目标方法调用逻辑
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);

生成完整的方法 :

protocol的refer:

代码语言:javascript
复制
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) {
    // 方法体
}

看起来太过抽象,如果没有很强的逻辑思维能力还是要写test类去尝试对 Protocol、LoadBalance 以及 Transporter 等接口的自适应拓展类代码生成过程进行调试。

比如下面就是用dubboProtocol输出的源码:

代码语言:javascript
复制
package com.alibaba.dubbo.rpc;

import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
    public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
        if (arg1 == null) throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg1;
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([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);
    }

    public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null)
            throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
        com.alibaba.dubbo.common.URL url = arg0.getUrl();
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
        com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.export(arg0);
    }

    public void destroy() {
        throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }

    public int getDefaultPort() {
        throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-11-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JathonKatu 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档