private void assignProperties()

in dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java [780:939]


    private void assignProperties(
            Object obj,
            Environment environment,
            Map<String, String> properties,
            InmemoryConfiguration configuration,
            ConfigMode configMode) {
        // if old one (this) contains non-null value, do not override
        boolean overrideIfAbsent = configMode == ConfigMode.OVERRIDE_IF_ABSENT;

        // even if old one (this) contains non-null value, do override
        boolean overrideAll = configMode == ConfigMode.OVERRIDE_ALL;

        FrameworkModel frameworkModel = ScopeModelUtil.getFrameworkModel(getScopeModel());

        // loop methods, get override value and set the new value back to method
        List<Method> methods =
                MethodUtils.getMethods(obj.getClass(), method -> method.getDeclaringClass() != Object.class);
        for (Method method : methods) {
            // filter non attribute
            Parameter parameter = method.getAnnotation(Parameter.class);
            if (parameter != null && !parameter.attribute()) {
                continue;
            }

            if (isPropertySetter(method)) {
                String propertyName = extractPropertyName(method.getName());

                // if config mode is OVERRIDE_IF_ABSENT and property has set, skip
                if (overrideIfAbsent && isPropertySet(methods, propertyName)) {
                    continue;
                }

                // convert camelCase/snake_case to kebab-case
                String kebabPropertyName = StringUtils.convertToSplitName(propertyName, "-");

                try {
                    String value = StringUtils.trim(configuration.getString(kebabPropertyName));

                    Class<?> paramType = method.getParameterTypes()[0];
                    if (paramType.isArray()) {
                        if (isIgnoredAttribute(obj.getClass(), propertyName)) {
                            continue;
                        }

                        Class<?> itemType = paramType.getComponentType();
                        List<Object> items = new ArrayList<>();
                        if (StringUtils.hasText(value)) {
                            value = environment.resolvePlaceholders(value);
                            if (StringUtils.hasText(value)) {
                                for (String item : StringUtils.tokenize(value, ',')) {
                                    items.add(ClassUtils.convertPrimitive(frameworkModel, itemType, item));
                                }
                            }
                        } else {
                            for (int i = 0; ; i++) {
                                value = StringUtils.trim(configuration.getString(kebabPropertyName + '[' + i + ']'));
                                if (value == null) {
                                    break;
                                }
                                if (StringUtils.hasText(value)) {
                                    value = environment.resolvePlaceholders(value);
                                    if (StringUtils.hasText(value)) {
                                        items.add(ClassUtils.convertPrimitive(frameworkModel, itemType, value));
                                    }
                                }
                            }
                        }
                        int len = items.size();
                        if (len > 0) {
                            Object array = Array.newInstance(itemType, len);
                            for (int i = 0; i < len; i++) {
                                Array.set(array, i, items.get(i));
                            }
                            method.invoke(obj, array);
                        }
                        continue;
                    }

                    // isTypeMatch() is called to avoid duplicate and incorrect update, for example, we have two
                    // 'setGeneric' methods in ReferenceConfig.
                    if (StringUtils.hasText(value)
                            && ClassUtils.isTypeMatch(paramType, value)
                            && !isIgnoredAttribute(obj.getClass(), propertyName)) {
                        value = environment.resolvePlaceholders(value);
                        if (StringUtils.hasText(value)) {
                            Object arg = ClassUtils.convertPrimitive(frameworkModel, paramType, value);
                            if (arg != null) {
                                method.invoke(obj, arg);
                            }
                        }
                    }
                } catch (Exception e) {
                    logger.info("Failed to override the property " + method.getName() + " in "
                            + obj.getClass().getSimpleName()
                            + ", please make sure every property has getter/setter method provided.");
                }
            } else if (isParametersSetter(method)) {
                String propertyName = extractPropertyName(method.getName());

                String value = StringUtils.trim(configuration.getString(propertyName));
                Map<String, String> parameterMap;
                if (StringUtils.hasText(value)) {
                    parameterMap = StringUtils.parseParameters(value);
                } else {
                    // in this case, maybe parameters.item3=value3.
                    parameterMap = ConfigurationUtils.getSubProperties(properties, PARAMETERS);
                }
                Map<String, String> newMap = convert(parameterMap, "");
                if (CollectionUtils.isEmptyMap(newMap)) {
                    continue;
                }

                // get old map from original obj
                Map<String, String> oldMap = null;
                try {
                    String getterName = calculatePropertyToGetter(propertyName);
                    Method getterMethod = this.getClass().getMethod(getterName);
                    Object oldOne = getterMethod.invoke(this);
                    if (oldOne instanceof Map) {
                        oldMap = (Map) oldOne;
                    }
                } catch (Exception ignore) {

                }

                // if old map is null, directly set params
                if (oldMap == null) {
                    invokeSetParameters(newMap, obj);
                    continue;
                }

                // if mode is OVERRIDE_IF_ABSENT, put all old map entries to new map, will override the same key
                // if mode is OVERRIDE_ALL, put all keyed entries not in new map from old map to new map (ignore the
                // same key appeared in old map)
                // if mode is others, override with new map
                if (overrideIfAbsent) {
                    newMap.putAll(oldMap);
                } else if (overrideAll) {
                    oldMap.forEach(newMap::putIfAbsent);
                }

                invokeSetParameters(newMap, obj);
            } else if (isNestedSetter(obj, method)) {
                try {
                    Class<?> clazz = method.getParameterTypes()[0];
                    Object inner = clazz.getDeclaredConstructor().newInstance();
                    String fieldName = MethodUtils.extractFieldName(method);
                    Map<String, String> subProperties = ConfigurationUtils.getSubProperties(properties, fieldName);
                    InmemoryConfiguration subPropsConfiguration = new InmemoryConfiguration(subProperties);
                    assignProperties(inner, environment, subProperties, subPropsConfiguration, configMode);
                    method.invoke(obj, inner);
                } catch (ReflectiveOperationException e) {
                    throw new IllegalStateException(
                            "Cannot assign nested class when refreshing config: "
                                    + obj.getClass().getName(),
                            e);
                }
            }
        }
    }