Dubbo Config

2018-09-15  本文已影响0人  outwar

Dubbo Config

AbstractConfig

这是dubbo所有的配置类的基类,除了id外没有实例属性了,主要还是提供了一些通用的方法。

protected static void appendProperties(AbstractConfig config) {
    if (config == null) {
        return;
    }
    //通过dubbo.+去掉后缀的类型+.得到前缀
    //例如ServiceConfig得到的就是dubbo.service.
    String prefix = "dubbo." + getTagName(config.getClass()) + ".";
    //遍历所有public方法
    Method[] methods = config.getClass().getMethods();
    for (Method method : methods) {
        try {
            String name = method.getName();
            //set方法
            if (name.length() > 3 && name.startsWith("set") && Modifier.isPublic(method.getModifiers())
                    && method.getParameterTypes().length == 1 && isPrimitive(method.getParameterTypes()[0])) {
                        //用.把驼峰命名隔开
                String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), ".");

                String value = null;
                if (config.getId() != null && config.getId().length() > 0) {
                    //有多个配置的时候通过id指定,例如dubbo.registry.a.timeout
                    String pn = prefix + config.getId() + "." + property;
                    //是否有环境变量
                    value = System.getProperty(pn);
                    if (!StringUtils.isBlank(value)) {
                        logger.info("Use System Property " + pn + " to config dubbo");
                    }
                }
                if (value == null || value.length() == 0) {
                    //默认的属性,类似上例dubbo.registry.timeout
                    String pn = prefix + property;
                    value = System.getProperty(pn);
                    if (!StringUtils.isBlank(value)) {
                        logger.info("Use System Property " + pn + " to config dubbo");
                    }
                }
                if (value == null || value.length() == 0) {
                    Method getter;
                    try {
                        getter = config.getClass().getMethod("get" + name.substring(3));
                    } catch (NoSuchMethodException e) {
                        try {
                            getter = config.getClass().getMethod("is" + name.substring(3));
                        } catch (NoSuchMethodException e2) {
                            getter = null;
                        }
                    }
                    //环境变量没有得到就使用get方法
                    if (getter != null) {
                        //已经可以通过get方法得到了表明已经通过项目的配置文件配置了该属性
                        if (getter.invoke(config) == null) {
                            //如果get方法返回为空就加载dubbo.properties文件,该文件路径可以通过dubbo.properties.file键修改
                            //dubbo.properties文件中的配置也是先拿指定id的
                            if (config.getId() != null && config.getId().length() > 0) {
                                value = ConfigUtils.getProperty(prefix + config.getId() + "." + property);
                            }
                            if (value == null || value.length() == 0) {
                                value = ConfigUtils.getProperty(prefix + property);
                            }
                            if (value == null || value.length() == 0) {
                                String legacyKey = legacyProperties.get(prefix + property);
                                if (legacyKey != null && legacyKey.length() > 0) {
                                    value = convertLegacyValue(legacyKey, ConfigUtils.getProperty(legacyKey));
                                }
                            }

                        }
                    }
                }
                //通过set方法注入替换
                if (value != null && value.length() > 0) {
                    method.invoke(config, convertPrimitive(method.getParameterTypes()[0], value));
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }
}

该方法的作用是通过反射添加属性,这里解释了为什么可以通过中间添加id配置,例如dubbo.registry.a.timeout,以及默认是没有id的。还有环境变量优先级最高,项目的配置文件次之,dubbo.properties文件是被优先级最低被加载的,该文件可以通过dubbo.properties.file指定修改。

@SuppressWarnings("unchecked")
protected static void appendParameters(Map<String, String> parameters, Object config, String prefix) {
    if (config == null) {
        return;
    }
    //遍历所有public方法
    Method[] methods = config.getClass().getMethods();
    for (Method method : methods) {
        try {
            String name = method.getName();
            //判断是否是get或者is方法,不是object的getClass方法,入参个数为0,返回类型是原始的或者包装类
            if ((name.startsWith("get") || name.startsWith("is"))
                    && !"getClass".equals(name)
                    && Modifier.isPublic(method.getModifiers())
                    && method.getParameterTypes().length == 0
                    && isPrimitive(method.getReturnType())) {
                        //Parameter注解
                Parameter parameter = method.getAnnotation(Parameter.class);
                //注解排除为true说明不需要注入,继续下个循环
                if (method.getReturnType() == Object.class || parameter != null && parameter.excluded()) {
                    continue;
                }
                //get的长度为3,is长度2
                int i = name.startsWith("get") ? 3 : 2;
                //用.将方法名默认为驼峰命名并隔开
                String prop = StringUtils.camelToSplitName(name.substring(i, i + 1).toLowerCase() + name.substring(i + 1), ".");
                String key;
                //注解制定的key或者经过处理的方法名
                if (parameter != null && parameter.key().length() > 0) {
                    key = parameter.key();
                } else {
                    key = prop;
                }
                //反射调用该方法
                Object value = method.invoke(config);
                String str = String.valueOf(value).trim();
                if (value != null && str.length() > 0) {
                    //url编码
                    if (parameter != null && parameter.escaped()) {
                        str = URL.encode(str);
                    }
                    //注解append为true表明该值需要添加
                    if (parameter != null && parameter.append()) {
                        //defalut值
                        String pre = parameters.get(Constants.DEFAULT_KEY + "." + key);
                        if (pre != null && pre.length() > 0) {
                            str = pre + "," + str;
                        }
                        //自身值
                        pre = parameters.get(key);
                        if (pre != null && pre.length() > 0) {
                            str = pre + "," + str;
                        }
                    }
                    //指定前缀
                    if (prefix != null && prefix.length() > 0) {
                        key = prefix + "." + key;
                    }
                    parameters.put(key, str);
                    //required为true但是该值为空抛出异常
                } else if (parameter != null && parameter.required()) {
                    throw new IllegalStateException(config.getClass().getSimpleName() + "." + key + " == null");
                }
                //getParameters方法的处理
            } else if ("getParameters".equals(name)
                    && Modifier.isPublic(method.getModifiers())
                    && method.getParameterTypes().length == 0
                    && method.getReturnType() == Map.class) {
                Map<String, String> map = (Map<String, String>) method.invoke(config, new Object[0]);
                if (map != null && map.size() > 0) {
                    String pre = (prefix != null && prefix.length() > 0 ? prefix + "." : "");
                    for (Map.Entry<String, String> entry : map.entrySet()) {
                        parameters.put(pre + entry.getKey().replace('-', '.'), entry.getValue());
                    }
                }
            }
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }
}

这个方法用来把dubbo的配置类中的属性放到map中,然后把这些属性添加到dubbo url中。

protected void appendAnnotation(Class<?> annotationClass, Object annotation) {
    //反射遍历注解的方法
    Method[] methods = annotationClass.getMethods();
    for (Method method : methods) {
        //不是Object的方法,不是返回void,参数个数为0,public,非static
        if (method.getDeclaringClass() != Object.class
                && method.getReturnType() != void.class
                && method.getParameterTypes().length == 0
                && Modifier.isPublic(method.getModifiers())
                && !Modifier.isStatic(method.getModifiers())) {
            try {
                //方法名字
                String property = method.getName();
                //两个方法的特殊处理,因为都是描述interface用的
                if ("interfaceClass".equals(property) || "interfaceName".equals(property)) {
                    property = "interface";
                }
                //同样名字的set方法
                String setter = "set" + property.substring(0, 1).toUpperCase() + property.substring(1);
                //对注解对象反射调用该方法
                Object value = method.invoke(annotation);
                if (value != null && !value.equals(method.getDefaultValue())) {
                    //方法返回类型(原始类型转为包装类型))
                    Class<?> parameterType = ReflectUtils.getBoxedClass(method.getReturnType());
                    //filter和listener是String[]类型的,转为String
                    if ("filter".equals(property) || "listener".equals(property)) {
                        parameterType = String.class;
                        value = StringUtils.join((String[]) value, ",");
                        //parameters转为键值一一对应的map
                    } else if ("parameters".equals(property)) {
                        parameterType = Map.class;
                        value = CollectionUtils.toStringMap((String[]) value);
                    }
                    try {
                        //对当前类的set方法进行注入,即将注解的属性注入当前对象
                        Method setterMethod = getClass().getMethod(setter, parameterType);
                        setterMethod.invoke(this, value);
                    } catch (NoSuchMethodException e) {
                    }
                }
            } catch (Throwable e) {
                logger.error(e.getMessage(), e);
            }
        }
    }
}

这个方法用来将Reference和Service注解的值通过相同名字的set方法注入自身对象,注意这里必须不等于默认值,像retries默认等于0的就不能通过设置为0关闭重试,可以设置-1关闭重试。该类还有一个toString方法,没有细看,不过只要debug就能看到这些配置对象的toString的值了。

dubboconfig.png
上一篇下一篇

猜你喜欢

热点阅读