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