这个类定义了很多分析配置的工具方法。
总体而言,比较简单,除了一些细节,没有一些值得研究的思想。
1、设计。
这个值得说一下的也就这个继承机制了:
Config的继承的机制
主要的的方法:
appendParameters:分析对象获取参数。调用类的get开头方法,获取到值,以方法名为key,值为value,然后添加到传参map parameter。
convertMethodConfig2AsyncInfo:转换对象的方法。
extractPropertyName:解析属性名。通过get、set方法。
appendAnnotation:获取对象的有返回值、无参、非静态、公共的方法,调用获取到值。然后调用这个”set" + 方法名的方法,把这个值set进去。
refresh:刷新配置。也就是属性注入,从复合配置保存容器中获取这个配置需要的属性值(就是看set方法),然后设置注入进去。
getMetaData:获取对象的有返回值、无参、非静态、公共的方法,调用获取到值。然后调用这个”set" + 方法名的方法,把这个值set进去。
详细代码解读注释如下:
/**
* Utility methods and public methods for parsing configuration
*
* @export
*/
public abstract class AbstractConfig implements Serializable {
protected static final Logger logger = LoggerFactory.getLogger(AbstractConfig.class);
private static final long serialVersionUID = 4267533505537413570L;
// 这个map保存历史遗留属性,随着dubbo变更,有些属性名发生过变迁
/**
* The legacy properties container
*/
private static final Map<String, String> LEGACY_PROPERTIES = new HashMap<String, String>();
/**
* The suffix container
*/
private static final String[] SUFFIXES = new String[]{"Config", "Bean", "ConfigBase"};
static {
LEGACY_PROPERTIES.put("dubbo.protocol.name", "dubbo.service.protocol");
LEGACY_PROPERTIES.put("dubbo.protocol.host", "dubbo.service.server.host");
LEGACY_PROPERTIES.put("dubbo.protocol.port", "dubbo.service.server.port");
LEGACY_PROPERTIES.put("dubbo.protocol.threads", "dubbo.service.max.thread.pool.size");
LEGACY_PROPERTIES.put("dubbo.consumer.timeout", "dubbo.service.invoke.timeout");
LEGACY_PROPERTIES.put("dubbo.consumer.retries", "dubbo.service.max.retry.providers");
LEGACY_PROPERTIES.put("dubbo.consumer.check", "dubbo.service.allow.no.provider");
LEGACY_PROPERTIES.put("dubbo.service.url", "dubbo.service.address");
}
/**
* The config id
*/
protected String id;
protected String prefix;
protected final AtomicBoolean refreshed = new AtomicBoolean(false);
// 转换历史遗留值。但是这里只针对两种属性进行了特别处理,
// 其他都是直接返回
private static String convertLegacyValue(String key, String value) {
if (value != null && value.length() > 0) {
if ("dubbo.service.max.retry.providers".equals(key)) {
return String.valueOf(Integer.parseInt(value) - 1);
} else if ("dubbo.service.allow.no.provider".equals(key)) {
return String.valueOf(!Boolean.parseBoolean(value));
}
}
return value;
}
// 获取标签名。SUFFIXES后缀有:"Config", "Bean", "ConfigBase"。
// 再把驼峰命名,改成”-“分隔。
public static String getTagName(Class<?> cls) {
String tag = cls.getSimpleName();
for (String suffix : SUFFIXES) {
if (tag.endsWith(suffix)) {
tag = tag.substring(0, tag.length() - suffix.length());
break;
}
}
return StringUtils.camelToSplitName(tag, "-");
}
public static void appendParameters(Map<String, String> parameters, Object config) {
appendParameters(parameters, config, null);
}
// 分析config参数的所有get方法,调用获取到值value,然后以get方法的方法
// 名获取到属性名作为key,如果prefix前缀存在,则key,再前面再加上prefix
//+ "."前缀。之后存入传参parameters map。
// 如果方法上存在Parameter注解,则依据注解的属性,判断是否分析这个方
// 法;指定key;是否url编码value;是否不新增只是append叠加value;
// 如果方法上被getParameters存在这个注解,则说明不是单一parameter,而
//是一个集合map,直接调用,然后获取到的map,全部添加到传参parameters
//中。
@SuppressWarnings("unchecked")
public static void appendParameters(Map<String, String> parameters, Object config, String prefix) {
if (config == null) {
return;
}
Method[] methods = config.getClass().getMethods();
for (Method method : methods) {
try {
String name = method.getName();
if (MethodUtils.isGetter(method)) {
Parameter parameter = method.getAnnotation(Parameter.class);
if (method.getReturnType() == Object.class || parameter != null && parameter.excluded()) {
continue;
}
String key;
if (parameter != null && parameter.key().length() > 0) {
key = parameter.key();
} else {
key = calculatePropertyFromGetter(name);
}
Object value = method.invoke(config);
String str = String.valueOf(value).trim();
if (value != null && str.length() > 0) {
if (parameter != null && parameter.escaped()) {
str = URL.encode(str);
}
if (parameter != null && parameter.append()) {
String 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);
} else if (parameter != null && parameter.required()) {
throw new IllegalStateException(config.getClass().getSimpleName() + "." + key + " == null");
}
} else if (isParametersGetter(method)) {
Map<String, String> map = (Map<String, String>) method.invoke(config, new Object[0]);
parameters.putAll(convert(map, prefix));
}
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
// 这个方法和appendParameters很类似,只不过没有appendParameters方法
//的很多额外的逻辑。这个方法只是分析get方法,然后添加到parameters中。
//也没有驼峰命名转成”-“分隔。
//这个方法也被Deprecated注释了,所以可以忽略。
@Deprecated
protected static void appendAttributes(Map<String, Object> parameters, Object config) {
appendAttributes(parameters, config, null);
}
@Deprecated
protected static void appendAttributes(Map<String, Object> parameters, Object config, String prefix) {
if (config == null) {
return;
}
Method[] methods = config.getClass().getMethods();
for (Method method : methods) {
try {
Parameter parameter = method.getAnnotation(Parameter.class);
if (parameter == null || !parameter.attribute()) {
continue;
}
String name = method.getName();
if (MethodUtils.isGetter(method)) {
String key;
if (parameter.key().length() > 0) {
key = parameter.key();
} else {
key = calculateAttributeFromGetter(name);
}
Object value = method.invoke(config);
if (value != null) {
if (prefix != null && prefix.length() > 0) {
key = prefix + "." + key;
}
parameters.put(key, value);
}
}
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
// 转换对象,从MethodConfig对象转成AsyncMethodInfo对象,一系列get、set操作。暂且不看这两个对象是干什么用的,为什么要转换。
protected static AsyncMethodInfo convertMethodConfig2AsyncInfo(MethodConfig methodConfig) {
if (methodConfig == null || (methodConfig.getOninvoke() == null && methodConfig.getOnreturn() == null && methodConfig.getOnthrow() == null)) {
return null;
}
//check config conflict
if (Boolean.FALSE.equals(methodConfig.isReturn()) && (methodConfig.getOnreturn() != null || methodConfig.getOnthrow() != null)) {
throw new IllegalStateException("method config error : return attribute must be set true when onreturn or onthrow has been set.");
}
AsyncMethodInfo asyncMethodInfo = new AsyncMethodInfo();
asyncMethodInfo.setOninvokeInstance(methodConfig.getOninvoke());
asyncMethodInfo.setOnreturnInstance(methodConfig.getOnreturn());
asyncMethodInfo.setOnthrowInstance(methodConfig.getOnthrow());
try {
String oninvokeMethod = methodConfig.getOninvokeMethod();
if (StringUtils.isNotEmpty(oninvokeMethod)) {
asyncMethodInfo.setOninvokeMethod(getMethodByName(methodConfig.getOninvoke().getClass(), oninvokeMethod));
}
String onreturnMethod = methodConfig.getOnreturnMethod();
if (StringUtils.isNotEmpty(onreturnMethod)) {
asyncMethodInfo.setOnreturnMethod(getMethodByName(methodConfig.getOnreturn().getClass(), onreturnMethod));
}
String onthrowMethod = methodConfig.getOnthrowMethod();
if (StringUtils.isNotEmpty(onthrowMethod)) {
asyncMethodInfo.setOnthrowMethod(getMethodByName(methodConfig.getOnthrow().getClass(), onthrowMethod));
}
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
return asyncMethodInfo;
}
// 反射获取Method对象。ReflectUtils工具类,基本上每一个框架都有这个玩意。
private static Method getMethodByName(Class<?> clazz, String methodName) {
try {
return ReflectUtils.findMethodByMethodName(clazz, methodName);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
// 获取子属性。比如a.b.c,a.b.d,然后传参a.b, 获取到c、d。
protected static Set<String> getSubProperties(Map<String, String> properties, String prefix) {
return properties.keySet().stream().filter(k -> k.contains(prefix)).map(k -> {
k = k.substring(prefix.length());
return k.substring(0, k.indexOf("."));
}).collect(Collectors.toSet());
}
// 提取属性名
private static String extractPropertyName(Class<?> clazz, Method setter) throws Exception {
String propertyName = setter.getName().substring("set".length());
Method getter = null;
try {
getter = clazz.getMethod("get" + propertyName);
} catch (NoSuchMethodException e) {
getter = clazz.getMethod("is" + propertyName);
}
Parameter parameter = getter.getAnnotation(Parameter.class);
if (parameter != null && StringUtils.isNotEmpty(parameter.key()) && parameter.useKeyAsProperty()) {
propertyName = parameter.key();
} else {
propertyName = propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1);
}
return propertyName;
}
private static String calculatePropertyFromGetter(String name) {
int i = name.startsWith("get") ? 3 : 2;
return StringUtils.camelToSplitName(name.substring(i, i + 1).toLowerCase() + name.substring(i + 1), ".");
}
private static String calculateAttributeFromGetter(String getter) {
int i = getter.startsWith("get") ? 3 : 2;
return getter.substring(i, i + 1).toLowerCase() + getter.substring(i + 1);
}
private static void invokeSetParameters(Class c, Object o, Map map) {
try {
Method method = findMethodByMethodSignature(c, "setParameters", new String[]{Map.class.getName()});
if (method != null && isParametersSetter(method)) {
method.invoke(o, map);
}
} catch (Throwable t) {
// ignore
}
}
private static Map<String, String> invokeGetParameters(Class c, Object o) {
try {
Method method = findMethodByMethodSignature(c, "getParameters", null);
if (method != null && isParametersGetter(method)) {
return (Map<String, String>) method.invoke(o);
}
} catch (Throwable t) {
// ignore
}
return null;
}
private static boolean isParametersGetter(Method method) {
String name = method.getName();
return ("getParameters".equals(name)
&& Modifier.isPublic(method.getModifiers())
&& method.getParameterTypes().length == 0
&& method.getReturnType() == Map.class);
}
private static boolean isParametersSetter(Method method) {
return ("setParameters".equals(method.getName())
&& Modifier.isPublic(method.getModifiers())
&& method.getParameterCount() == 1
&& Map.class == method.getParameterTypes()[0]
&& method.getReturnType() == void.class);
}
private static Map<String, String> convert(Map<String, String> parameters, String prefix) {
if (parameters == null || parameters.isEmpty()) {
return Collections.emptyMap();
}
Map<String, String> result = new HashMap<>();
String pre = (prefix != null && prefix.length() > 0 ? prefix + "." : "");
for (Map.Entry<String, String> entry : parameters.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
result.put(pre + key, value);
// For compatibility, key like "registry-type" will has a duplicate key "registry.type"
if (key.contains("-")) {
result.put(pre + key.replace('-', '.'), value);
}
}
return result;
}
@Parameter(excluded = true)
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public void updateIdIfAbsent(String value) {
if (StringUtils.isNotEmpty(value) && StringUtils.isEmpty(id)) {
this.id = value;
}
}
// 获取对象的有返回值、无参、非静态、公共的方法,调用获取到值。
// 然后调用这个”set" + 方法名的方法,把这个值set进去。
protected void appendAnnotation(Class<?> annotationClass, Object annotation) {
Method[] methods = annotationClass.getMethods();
for (Method method : methods) {
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();
if ("interfaceClass".equals(property) || "interfaceName".equals(property)) {
property = "interface";
}
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());
if ("filter".equals(property) || "listener".equals(property)) {
parameterType = String.class;
value = StringUtils.join((String[]) value, ",");
} else if ("parameters".equals(property)) {
parameterType = Map.class;
value = CollectionUtils.toStringMap((String[]) value);
}
try {
Method setterMethod = getClass().getMethod(setter, parameterType);
setterMethod.invoke(this, value);
} catch (NoSuchMethodException e) {
// ignore
}
}
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
}
}
/**
* Should be called after Config was fully initialized.
* // FIXME: this method should be completely replaced by appendParameters
*
* @return
* @see AbstractConfig#appendParameters(Map, Object, String)
* <p>
* Notice! This method should include all properties in the returning map, treat @Parameter differently compared to appendParameters.
*/
// 获取metaData,就是把get、is方法,调用获取到值,保存到一个对象然后返回。
public Map<String, String> getMetaData() {
Map<String, String> metaData = new HashMap<>();
Method[] methods = this.getClass().getMethods();
for (Method method : methods) {
try {
String name = method.getName();
if (MethodUtils.isMetaMethod(method)) {
String key;
Parameter parameter = method.getAnnotation(Parameter.class);
if (parameter != null && parameter.key().length() > 0 && parameter.useKeyAsProperty()) {
key = parameter.key();
} else {
key = calculateAttributeFromGetter(name);
}
// treat url and configuration differently, the value should always present in configuration though it may not need to present in url.
//if (method.getReturnType() == Object.class || parameter != null && parameter.excluded()) {
if (method.getReturnType() == Object.class) {
metaData.put(key, null);
continue;
}
/**
* Attributes annotated as deprecated should not override newly added replacement.
*/
if (MethodUtils.isDeprecated(method) && metaData.get(key) != null) {
continue;
}
Object value = method.invoke(this);
String str = String.valueOf(value).trim();
if (value != null && str.length() > 0) {
metaData.put(key, str);
} else {
metaData.put(key, null);
}
} else if (isParametersGetter(method)) {
Map<String, String> map = (Map<String, String>) method.invoke(this, new Object[0]);
metaData.putAll(convert(map, ""));
}
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
return metaData;
}
@Parameter(excluded = true)
public String getPrefix() {
return StringUtils.isNotEmpty(prefix) ? prefix : (CommonConstants.DUBBO + "." + getTagName(this.getClass()));
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
// 刷新配置。也就是,从符合配置保存类中获取这个配置需要的属性值(就是看set方法),然后设置进去。
public void refresh() {
Environment env = ApplicationModel.getEnvironment();
try {
CompositeConfiguration compositeConfiguration = env.getPrefixedConfiguration(this);
// loop methods, get override value and set the new value back to method
Method[] methods = getClass().getMethods();
for (Method method : methods) {
if (MethodUtils.isSetter(method)) {
try {
String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
// isTypeMatch() is called to avoid duplicate and incorrect update, for example, we have two 'setGeneric' methods in ReferenceConfig.
if (StringUtils.isNotEmpty(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
}
} catch (NoSuchMethodException e) {
logger.info("Failed to override the property " + method.getName() + " in " +
this.getClass().getSimpleName() +
", please make sure every property has getter/setter method provided.");
}
} else if (isParametersSetter(method)) {
String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
if (StringUtils.isNotEmpty(value)) {
Map<String, String> map = invokeGetParameters(getClass(), this);
map = map == null ? new HashMap<>() : map;
map.putAll(convert(StringUtils.parseParameters(value), ""));
invokeSetParameters(getClass(), this, map);
}
}
}
} catch (Exception e) {
logger.error("Failed to override ", e);
}
}
@Override
public String toString() {
try {
StringBuilder buf = new StringBuilder();
buf.append("<dubbo:");
buf.append(getTagName(getClass()));
Method[] methods = getClass().getMethods();
for (Method method : methods) {
try {
if (MethodUtils.isGetter(method)) {
String name = method.getName();
String key = calculateAttributeFromGetter(name);
try {
getClass().getDeclaredField(key);
} catch (NoSuchFieldException e) {
// ignore
continue;
}
Object value = method.invoke(this);
if (value != null) {
buf.append(" ");
buf.append(key);
buf.append("=\"");
buf.append(value);
buf.append("\"");
}
}
} catch (Exception e) {
logger.warn(e.getMessage(), e);
}
}
buf.append(" />");
return buf.toString();
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
return super.toString();
}
}
/**
* FIXME check @Parameter(required=true) and any conditions that need to match.
*/
@Parameter(excluded = true)
public boolean isValid() {
return true;
}
// 判断相等,就是根据各个get方法的值是否相等。
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj.getClass().getName().equals(this.getClass().getName()))) {
return false;
}
Method[] methods = this.getClass().getMethods();
for (Method method1 : methods) {
if (MethodUtils.isGetter(method1)) {
Parameter parameter = method1.getAnnotation(Parameter.class);
if (parameter != null && parameter.excluded()) {
continue;
}
try {
Method method2 = obj.getClass().getMethod(method1.getName(), method1.getParameterTypes());
Object value1 = method1.invoke(this, new Object[]{});
Object value2 = method2.invoke(obj, new Object[]{});
if (!Objects.equals(value1, value2)) {
return false;
}
} catch (Exception e) {
return true;
}
}
}
return true;
}
/**
* Add {@link AbstractConfig instance} into {@link ConfigManager}
* <p>
* Current method will invoked by Spring or Java EE container automatically, or should be triggered manually.
*
* @see ConfigManager#addConfig(AbstractConfig)
* @since 2.7.5
*/
@PostConstruct
public void addIntoConfigManager() {
ApplicationModel.getConfigManager().addConfig(this);
}
@Override
public int hashCode() {
int hashCode = 1;
Method[] methods = this.getClass().getMethods();
for (Method method : methods) {
if (MethodUtils.isGetter(method)) {
Parameter parameter = method.getAnnotation(Parameter.class);
if (parameter != null && parameter.excluded()) {
continue;
}
try {
Object value = method.invoke(this, new Object[]{});
hashCode = 31 * hashCode + value.hashCode();
} catch (Exception ignored) {
//ignored
}
}
}
if (hashCode == 0) {
hashCode = 1;
}
return hashCode;
}
}