dubbo源码---服务暴露(2)

                                                            组装URL

  本节,全部介绍:doExportUrlsForlProtocol()方法,位于ServiceConfig类中,我们分为四个段落,进行阅读

我们先看一下这个方法的传入参数:ProtocolConfig protocolConfig, List<URL> registryURLs

第一个参数是协议,它也继承自AbstractConfig   第二个是URL(映射到一个注册中心)

看一下部分字段 ProcotolConfig类 ,字段真的超级多

public class ProtocolConfig extends AbstractConfig {
    private String name; //protocol name
    private String host; //service ip address
    private Integer port;
    private String contextpath;
    private String threadpool;
    private String threadname;
    private Integer corethreads; //threadpool size
    private Integer threads;    //max size
    private Integer iothreads; //io size
...
}

第一段

        String name = protocolConfig.getName();
        //如果协议为空,则将协议名设置为 dubbo
        if (StringUtils.isEmpty(name)) {
            name = DUBBO;
        }

        Map<String, String> map = new HashMap<String, String>();
        map.put(SIDE_KEY, PROVIDER_SIDE); //添加side
        //通过反射,将对象的信息放到map中,用于后续构造URL,
// 1号
        ServiceConfig.appendRuntimeParameters(map);
// 2号
        AbstractConfig.appendParameters(map, getApplication()); //applicationConfig
        AbstractConfig.appendParameters(map, getModule());  //ModuleConfig
        AbstractConfig.appendParameters(map, provider);  //ProviderConfig
        AbstractConfig.appendParameters(map, protocolConfig); //protocolConfig
        AbstractConfig.appendParameters(map, this);

        MetadataReportConfig metadataReportConfig = getMetadataReportConfig();
        if (metadataReportConfig != null && metadataReportConfig.isValid()) {
            map.putIfAbsent(METADATA_KEY, REMOTE_METADATA_STORAGE_TYPE);
        }

 

第二段

 方法参数设置,检测 <dubbo:method> 标签中的配置信息,并将相关配置添加到 map 中,MethodConfig 中存储了 <dubbo:method> 标签的配置信息,这个标签是<dubbo:service>下面的子标签

一般我们写的,没有使用method,而是直接暴露实现类,所以该段可以跳过。  第二段,比较纯粹,没有涉及到其他未知的方法


if (CollectionUtils.isNotEmpty(getMethods())) {
    for (MethodConfig method : getMethods()) {       // 【第一层】 for循环
        AbstractConfig.appendParameters(map, method, method.getName());  //服务粒度 到达方法
        String retryKey = method.getName() + ".retry";
        if (map.containsKey(retryKey)) {
            String retryValue = map.remove(retryKey);
            if ("false".equals(retryValue)) {
                map.put(method.getName() + ".retries", "0");
            }
        }
        List<ArgumentConfig> arguments = method.getArguments();  //获取方法参数(自定义的 方法参数类,内设置了下标等属性)
        if (CollectionUtils.isNotEmpty(arguments)) {
            for (ArgumentConfig argument : arguments) {   //【第二层】  遍历参数
                // 检测 type是否为空
                if (argument.getType() != null && argument.getType().length() > 0) {
                    Method[] methods = interfaceClass.getMethods();  //获得接口方法
                    // 保证,暴露的方法 能对应上接口的方法,不然该方法是无效的(  安全防护)
                    if (methods.length > 0) {
                        for (int i = 0; i < methods.length; i++) {   //【第三层】 遍历接口方法
                            String methodName = methods[i].getName();
                            if (methodName.equals(method.getName())) {   //找到实现类和接口方法对应的 (双重遍历)
                                Class<?>[] argtypes = methods[i].getParameterTypes(); //获取接口方法的参数类型
                                //
                                if (argument.getIndex() != -1) {  //参数 顺序要对齐
                                    if (argtypes[argument.getIndex()].getName().equals(argument.getType())) {
                                        AbstractConfig.appendParameters(map, argument, method.getName() + "." + argument.getIndex()); //将信息,放入 map
                                    } else {
                                        throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
                                    }
                                } else { //如果没有对齐,遍历一下,是否存在参数
                                    for (int j = 0; j < argtypes.length; j++) {
                                        Class<?> argclazz = argtypes[j];
                                        if (argclazz.getName().equals(argument.getType())) {
                                            AbstractConfig.appendParameters(map, argument, method.getName() + "." + j);
                                            if (argument.getIndex() != -1 && argument.getIndex() != j) {
                                                throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                } else if (argument.getIndex() != -1) {
                    AbstractConfig.appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                } else {
                    throw new IllegalArgumentException("Argument config must set index or type attribute.eg: <dubbo:argument index='0' .../> or <dubbo:argument type=xxx .../>");
                }

            }
        }
    } // end of methods for
}

第三段

    主要是检测一些配置,是否正确,并且进行URL的组装,重点是对接口中的方法进行处理,

if (ProtocolUtils.isGeneric(generic)) {
    map.put(GENERIC_KEY, generic);
    map.put(METHODS_KEY, ANY_VALUE);
} else {
    String revision = Version.getVersion(interfaceClass, version);
    if (revision != null && revision.length() > 0) {
        map.put(REVISION_KEY, revision);
    }
// 为接口生成包裹类 Wrapper,Wrapper 中包含了接口的详细信息,比如接口方法名数组,字段信息等
    String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();  //  【入】
    // 添加方法名到 map 中,如果包含多个方法名,则用逗号隔开,比如 method = init,destroy
    if (methods.length == 0) {
        logger.warn("No method found in service interface " + interfaceClass.getName());
        map.put(METHODS_KEY, ANY_VALUE);
    } else {
        map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
    }
}

if(ConfigUtils.isEmpty(token) && provider != null) {
    token = provider.getToken();
}
if (!ConfigUtils.isEmpty(token)) {
    if (ConfigUtils.isDefault(token)) {
        map.put(TOKEN_KEY, UUID.randomUUID().toString());
    } else {
        map.put(TOKEN_KEY, token);
    }
}
//init serviceMetadata attachments
serviceMetadata.getAttachments().putAll(map);
String host = findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = findConfigedPorts(protocolConfig, name, map);
//【url组装】
URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);

我们 看一下,如何包装interfaceClass到wrapper中的   Wrapper类

    public static Wrapper getWrapper(Class<?> c) {
        //从缓冲中获取,
        while (ClassGenerator.isDynamicClass(c)) // can not wrapper on dynamic class.
        {
            c = c.getSuperclass();
        }
        if (c == Object.class) {
            return OBJECT_WRAPPER;
        }
        //未命中,创建一个,并且放入缓冲
        return WRAPPER_MAP.computeIfAbsent(c, key -> makeWrapper(key)); //【入】
    }

如果缓冲不存在,则需要makeWrapper,这里就有很多代码了!它应该是一种代理操作,我们需要分成5段阅读

3.1 makeWrapper()第一段 :容器准备

  可见,创建了三个方法的代码,存储在实体中。三个方法分别是针对类的字段,类的方法,字段具有set/get两个方法,由于我们的demo接口中,只有方法,所以重点在c3代码中。

     if (c.isPrimitive()) {  //检测c是否为基本类型,若是 则抛出异常
            throw new IllegalArgumentException("Can not create wrapper for primitive type: " + c);
        }

        String name = c.getName();
        ClassLoader cl = ClassUtils.getClassLoader(c);
        //c1用于存储setPropertyValue方法代码,下面两个以此内推
        StringBuilder c1 = new StringBuilder("public void setPropertyValue(Object o, String n, Object v){ ");
        StringBuilder c2 = new StringBuilder("public Object getPropertyValue(Object o, String n){ ");
        StringBuilder c3 = new StringBuilder("public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws " + InvocationTargetException.class.getName() + "{ ");
        //生成类型转换代码及异常捕捉代码
        c1.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
        c2.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
        c3.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");

        // pts 用于存储成员变量名和类型
        Map<String, Class<?>> pts = new HashMap<>(); // <property name, property types>
        // ms 用于存储方法描述信息(方法签名),以及method实例
        Map<String, Method> ms = new LinkedHashMap<>(); // <method desc, Method instance>
        //mns 方法名字
        List<String> mns = new ArrayList<>(); // method names.
        //dmns 用于存储“定义在当前类中的方法” 的名称
        List<String> dmns = new ArrayList<>(); // declaring method names.

看一个setPropertyValue()方法的代码:它还没有完成,缺少 }

public void setPropertyValue(Object o, String n, Object v){ 
    org.apache.dubbo.demo.DemoService w; 
    try{ 
        w = ((org.apache.dubbo.demo.DemoService)$1); 
    }catch(Throwable e){ 
        throw new IllegalArgumentException(e); 
    }

3.2 makeWrapper() 第二段

获取全部public 字段,由于我们的demo中没有成员变量,所以跳过了,但是这里要记住,处理了接口中的字段!

for (Field f : c.getFields()) {
    String fn = f.getName();
    Class<?> ft = f.getType();
    if (Modifier.isStatic(f.getModifiers()) || Modifier.isTransient(f.getModifiers())) {
        continue;
    }
    //生成条件判断及赋值语句
    // if( $2.equals("name") ) { w.name = (java.lang.String) $3; return;}
    c1.append(" if( $2.equals(\"").append(fn).append("\") ){ w.").append(fn).append("=").append(arg(ft, "$3")).append("; return; }");
    c2.append(" if( $2.equals(\"").append(fn).append("\") ){ return ($w)w.").append(fn).append("; }");
    // 存储 <字段名, 字段类型> 键值对到 pts 中
    pts.put(fn, ft);
}

3.3 makeWrapper() 第三段:方法处理

  Method[] methods = c.getMethods();
        boolean hasMethod = hasMethods(methods); //是否有自己写的方法(除Object方法)
        if (hasMethod) {
            c3.append(" try{");
            for (Method m : methods) {
                //ignore(忽略) Object's method.
                if (m.getDeclaringClass() == Object.class) {
                    continue;
                }
                String mn = m.getName();
                //生成方法判断语句,比如: if("syaHello".equals($2)
                c3.append(" if( \"").append(mn).append("\".equals( $2 ) ");
                int len = m.getParameterTypes().length;
                c3.append(" && ").append(" $3.length == ").append(len);
                boolean override = false;
                for (Method m2 : methods) { //二层 【for】
                    if (m != m2 && m.getName().equals(m2.getName())) { //有重载,方法对象不同,名字一样
                        override = true;
                        break;
                    }
                }
                if (override) {
                    if (len > 0) {
                        for (int l = 0; l < len; l++) {
                            //生成参数类型 进行检测代码
                            c3.append(" && ").append(" $3[").append(l).append("].getName().equals(\"")
                                    .append(m.getParameterTypes()[l].getName()).append("\")");
                        }
                    }
                }

                c3.append(" ) { ");
                // 根据返回值类型生成目标方法调用语句
                if (m.getReturnType() == Void.TYPE) {
                    c3.append(" w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");").append(" return null;");
                } else {
                    c3.append(" return ($w)w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");");
                }

                c3.append(" }");
                // 添加方法名到 mns 集合中
                mns.add(mn);
                if (m.getDeclaringClass() == c) {
                    dmns.add(mn);
                }
                ms.put(ReflectUtils.getDesc(m), m);
            }
            c3.append(" } catch(Throwable e) { ");
            c3.append("     throw new java.lang.reflect.InvocationTargetException(e); ");
            c3.append(" }");
        }

        c3.append(" throw new " + NoSuchMethodException.class.getName() + "(\"Not found method \\\"\"+$2+\"\\\" in class " + c.getName() + ".\"); }");

处理完毕method之后,C3结构:

public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException{ 
    org.apache.dubbo.demo.DemoService w;
     try{ 
        w = ((org.apache.dubbo.demo.DemoService)$1); 
    }catch(Throwable e){ 
        throw new IllegalArgumentException(e); } 
    try{ 
        if( "sayHello".equals( $2 )  &&  $3.length == 1 ) {  
            return ($w)w.sayHello((java.lang.String)$4[0]); 
        } 
        if( "sayHelloAsync".equals( $2 )  &&  $3.length == 1 ) {  
            return ($w)w.sayHelloAsync((java.lang.String)$4[0]); 
        }
     } catch(Throwable e) {      
        throw new java.lang.reflect.InvocationTargetException(e);  
    } 
    throw new org.apache.dubbo.common.bytecode.NoSuchMethodException("Not found method \""+$2+"\" in class org.apache.dubbo.demo.DemoService."); 
}

3.4 makeWrapper第四段:处理get/set方法

Matcher matcher;
for (Map.Entry<String, Method> entry : ms.entrySet()) {
    String md = entry.getKey();
    Method method = entry.getValue();
    if ((matcher = ReflectUtils.GETTER_METHOD_DESC_PATTERN.matcher(md)).matches()) {
        String pn = propertyName(matcher.group(1));
        c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");
        pts.put(pn, method.getReturnType());
    } else if ((matcher = ReflectUtils.IS_HAS_CAN_METHOD_DESC_PATTERN.matcher(md)).matches()) {
        String pn = propertyName(matcher.group(1));
        c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");
        pts.put(pn, method.getReturnType());
    } else if ((matcher = ReflectUtils.SETTER_METHOD_DESC_PATTERN.matcher(md)).matches()) {
        Class<?> pt = method.getParameterTypes()[0];
        String pn = propertyName(matcher.group(1));
        c1.append(" if( $2.equals(\"").append(pn).append("\") ){ w.").append(method.getName()).append("(").append(arg(pt, "$3")).append("); return; }");
        pts.put(pn, pt);
    }
}
c1.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" field or setter method in class " + c.getName() + ".\"); }");
c2.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" field or setter method in class " + c.getName() + ".\"); }");

3.5 makeWrapper第五段:类生成

 long id = WRAPPER_CLASS_COUNTER.getAndIncrement();
        //创建 类生成器
        ClassGenerator cc = ClassGenerator.newInstance(cl);
        //设置 类名和超类
        cc.setClassName((Modifier.isPublic(c.getModifiers()) ? Wrapper.class.getName() : c.getName() + "$sw") + id);
        cc.setSuperClass(Wrapper.class);
        //添加构造方法,字段
        cc.addDefaultConstructor();
        cc.addField("public static String[] pns;"); // property name array.
        cc.addField("public static " + Map.class.getName() + " pts;"); // property type map.
        cc.addField("public static String[] mns;"); // all method name array.
        cc.addField("public static String[] dmns;"); // declared method name array.
        for (int i = 0, len = ms.size(); i < len; i++) {
            cc.addField("public static Class[] mts" + i + ";");
        }
        //添加方法代码
        cc.addMethod("public String[] getPropertyNames(){ return pns; }");
        cc.addMethod("public boolean hasProperty(String n){ return pts.containsKey($1); }");
        cc.addMethod("public Class getPropertyType(String n){ return (Class)pts.get($1); }");
        cc.addMethod("public String[] getMethodNames(){ return mns; }");
        cc.addMethod("public String[] getDeclaredMethodNames(){ return dmns; }");
        cc.addMethod(c1.toString());
        cc.addMethod(c2.toString());
        cc.addMethod(c3.toString());

        try {
            //生成类
            Class<?> wc = cc.toClass();
            //设置字段值
            wc.getField("pts").set(null, pts);
            wc.getField("pns").set(null, pts.keySet().toArray(new String[0]));
            wc.getField("mns").set(null, mns.toArray(new String[0]));
            wc.getField("dmns").set(null, dmns.toArray(new String[0]));
            int ix = 0;
            for (Method m : ms.values()) {
                wc.getField("mts" + ix++).set(null, m.getParameterTypes());
            }
            //创建wrapper实例
            return (Wrapper) wc.newInstance();
        }

最后,利用已存在的URL,+map生成新的URL,它是dubbo重写的,不是jdk的url

class URL implements Serializable {
    private static final long serialVersionUID = -1985165475234910535L;

    private final String protocol;
    private final String username;
    private final String password;
    // by default, host to registry
    private final String host;
    // by default, port to registry
    private final int port;
    private final String path;
    private final Map<String, String> parameters;
    private final Map<String, Map<String, String>> methodParameters;

    // ==== cache ====
    private volatile transient Map<String, Number> numbers;
    private volatile transient Map<String, Map<String, Number>> methodNumbers;
    private volatile transient Map<String, URL> urls;
    private volatile transient String ip;
    private volatile transient String full;
    private volatile transient String identity;
    private volatile transient String parameter;
    private volatile transient String string;
    private transient String serviceKey;
    private transient String address;
...
get/set
}

第四段

服务暴露

String scope = url.getParameter(SCOPE_KEY);
if (!SCOPE_NONE.equalsIgnoreCase(scope)) {

    if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
        exportLocal(url);   // ---------【本地服务暴露】-----------
    }
    if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
        if (CollectionUtils.isNotEmpty(registryURLs)) {
            for (URL registryURL : registryURLs) {
                //if protocol is only injvm ,not register
                if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
                    continue;
                }
                url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));
                URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryURL);
                if (monitorUrl != null) {
                    url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());
                }

                String proxy = url.getParameter(PROXY_KEY);
                if (StringUtils.isNotEmpty(proxy)) {
                    registryURL = registryURL.addParameter(PROXY_KEY, proxy);
                }
        //-----【 通过动态代理 转换成 invoker 】--------------
                Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
                DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
                //服务暴露后,向注册中心,注册信息 【RegistryProtocol?】 官方走的是这里,但是不知道是不是这个入口进去的!
                Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
                exporters.add(exporter);
            }
        } else { //没有处理注册中心场景,直接暴露服务
            Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
            DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

            Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
            exporters.add(exporter);
        }
 
        WritableMetadataService metadataService = WritableMetadataService.getExtension(url.getParameter(METADATA_KEY, DEFAULT_METADATA_STORAGE_TYPE));
        if (metadataService != null) {
            metadataService.publishServiceDefinition(url);
        }
    }
}
this.urls.add(url);

具体到每个协议的暴露,请看我们下一节:export()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值