[SCB-2280]service center address should support place holder with comma separated array value (#2422)

diff --git a/demo/demo-cse-v1/README.md b/demo/demo-cse-v1/README.md
index 29049b2..f87dfd6 100644
--- a/demo/demo-cse-v1/README.md
+++ b/demo/demo-cse-v1/README.md
@@ -9,6 +9,10 @@
   v1:
     test:
       foo: foo
+      dynamicString: a,b
+      dynamicArray:
+        - m
+        - n
 ```
 
 * 依次启动 provider、consumer、gateway
diff --git a/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigController.java b/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigController.java
index 4018e11..34a40bd 100644
--- a/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigController.java
+++ b/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigController.java
@@ -17,6 +17,8 @@
 
 package org.apache.servicecomb.samples;
 
+import java.util.List;
+
 import org.apache.servicecomb.provider.rest.common.RestSchema;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.env.Environment;
@@ -24,6 +26,8 @@
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 
+import com.netflix.config.DynamicPropertyFactory;
+
 @RestSchema(schemaId = "ConsumerConfigController")
 @RequestMapping(path = "/")
 public class ConsumerConfigController {
@@ -47,4 +51,21 @@
   public String bar() {
     return consumerConfigurationProperties.getBar();
   }
+
+  @GetMapping("/dynamicString")
+  public String dynamicString(@RequestParam("key") String key) {
+    return DynamicPropertyFactory.getInstance().getStringProperty(key, null).get();
+  }
+
+  @GetMapping("/dynamicArray")
+  @SuppressWarnings("unchecked")
+  public List<String> dynamicArray() {
+    return consumerConfigurationProperties.getDynamicArray();
+//     DynamicPropertyFactory & Environment do not support arrays like:
+//           key[0]: v0
+//           key[1]: v1
+//    return environment.getProperty(key, List.class);
+//    return Arrays.asList(((AbstractConfiguration) DynamicPropertyFactory.getBackingConfigurationSource())
+//        .getStringArray(key));
+  }
 }
diff --git a/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigurationProperties.java b/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigurationProperties.java
index c21ba63..6f95924 100644
--- a/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigurationProperties.java
+++ b/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigurationProperties.java
@@ -17,6 +17,8 @@
 
 package org.apache.servicecomb.samples;
 
+import java.util.List;
+
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.stereotype.Component;
 
@@ -27,6 +29,8 @@
 
   private String bar;
 
+  private List<String> dynamicArray;
+
   public String getFoo() {
     return foo;
   }
@@ -42,4 +46,12 @@
   public void setBar(String bar) {
     this.bar = bar;
   }
+
+  public List<String> getDynamicArray() {
+    return dynamicArray;
+  }
+
+  public void setDynamicArray(List<String> dynamicArray) {
+    this.dynamicArray = dynamicArray;
+  }
 }
diff --git a/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java b/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java
index ff03a44..30200b6 100644
--- a/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java
+++ b/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java
@@ -17,6 +17,8 @@
 
 package org.apache.servicecomb.samples;
 
+import java.util.List;
+
 import org.apache.servicecomb.demo.CategorizedTestCase;
 import org.apache.servicecomb.demo.TestMgr;
 import org.springframework.stereotype.Component;
@@ -32,11 +34,20 @@
     testFooBar();
   }
 
+  @SuppressWarnings("unchecked")
   private void testConfig() {
     String result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v1.test.foo", String.class);
     TestMgr.check("\"foo\"", result);
     result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v1.test.bar", String.class);
     TestMgr.check("\"bar\"", result);
+    result = template.getForObject(Config.GATEWAY_URL + "/dynamicString?key=cse.v1.test.dynamicString", String.class);
+    TestMgr.check("\"a,b\"", result);
+
+    List<String> listResult = template
+        .getForObject(Config.GATEWAY_URL + "/dynamicArray", List.class);
+    TestMgr.check(2, listResult.size());
+    TestMgr.check("m", listResult.get(0));
+    TestMgr.check("n", listResult.get(1));
   }
 
   private void testFooBar() {
diff --git a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/collect/ConfigCenterDefaultDeploymentProvider.java b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/collect/ConfigCenterDefaultDeploymentProvider.java
index a7c5ca8..287f1ac 100644
--- a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/collect/ConfigCenterDefaultDeploymentProvider.java
+++ b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/collect/ConfigCenterDefaultDeploymentProvider.java
@@ -17,14 +17,16 @@
 
 package org.apache.servicecomb.config.collect;
 
-import com.google.common.annotations.VisibleForTesting;
+import java.util.Arrays;
+import java.util.List;
+
 import org.apache.commons.configuration.AbstractConfiguration;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.servicecomb.config.ConfigUtil;
 import org.apache.servicecomb.deployment.DeploymentProvider;
 import org.apache.servicecomb.deployment.SystemBootstrapInfo;
 
-import java.util.Arrays;
+import com.google.common.annotations.VisibleForTesting;
 
 public class ConfigCenterDefaultDeploymentProvider implements DeploymentProvider {
   public static final String SYSTEM_KEY_CONFIG_CENTER = "ConfigCenter";
@@ -36,12 +38,13 @@
     if (!systemKey.equals(SYSTEM_KEY_CONFIG_CENTER)) {
       return null;
     }
-    String[] ccAddresses = configuration.getStringArray("servicecomb.config.client.serverUri");
-    if (ArrayUtils.isEmpty(ccAddresses)) {
+    List<String> ccAddresses = ConfigUtil
+        .parseArrayValue(configuration.getString("servicecomb.config.client.serverUri"));
+    if (ccAddresses.isEmpty()) {
       return null;
     }
     SystemBootstrapInfo cc = new SystemBootstrapInfo();
-    cc.setAccessURL(Arrays.asList(ccAddresses));
+    cc.setAccessURL(ccAddresses);
     return cc;
   }
 
diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/collect/KieCenterDefaultDeploymentProvider.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/collect/KieCenterDefaultDeploymentProvider.java
index 9488b67..9412314 100644
--- a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/collect/KieCenterDefaultDeploymentProvider.java
+++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/collect/KieCenterDefaultDeploymentProvider.java
@@ -17,14 +17,14 @@
 
 package org.apache.servicecomb.config.kie.collect;
 
-import com.google.common.annotations.VisibleForTesting;
+import java.util.List;
+
 import org.apache.commons.configuration.AbstractConfiguration;
-import org.apache.commons.lang3.ArrayUtils;
 import org.apache.servicecomb.config.ConfigUtil;
 import org.apache.servicecomb.deployment.DeploymentProvider;
 import org.apache.servicecomb.deployment.SystemBootstrapInfo;
 
-import java.util.Arrays;
+import com.google.common.annotations.VisibleForTesting;
 
 public class KieCenterDefaultDeploymentProvider implements DeploymentProvider {
   public static final String SYSTEM_KEY_KIE_CENTER = "KieCenter";
@@ -36,12 +36,13 @@
     if (!systemKey.equals(SYSTEM_KEY_KIE_CENTER)) {
       return null;
     }
-    String[] kieAddresses = configuration.getStringArray("servicecomb.kie.serverUri");
-    if (ArrayUtils.isEmpty(kieAddresses)) {
+    List<String> kieAddresses = ConfigUtil
+        .parseArrayValue(configuration.getString("servicecomb.kie.serverUri"));
+    if (kieAddresses.isEmpty()) {
       return null;
     }
     SystemBootstrapInfo kie = new SystemBootstrapInfo();
-    kie.setAccessURL(Arrays.asList(kieAddresses));
+    kie.setAccessURL(kieAddresses);
     return kie;
   }
 
diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConcurrentMapConfigurationExt.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConcurrentMapConfigurationExt.java
new file mode 100644
index 0000000..847342c
--- /dev/null
+++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConcurrentMapConfigurationExt.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.config;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.commons.configuration.AbstractConfiguration;
+
+import com.netflix.config.ConcurrentMapConfiguration;
+
+/**
+ * Disable delimiter parsing for string
+ */
+@SuppressWarnings("unchecked")
+public class ConcurrentMapConfigurationExt extends ConcurrentMapConfiguration {
+  public ConcurrentMapConfigurationExt() {
+    super();
+    setDelimiterParsingDisabled(true);
+  }
+
+  public ConcurrentMapConfigurationExt(Map<String, Object> mapToCopy) {
+    super();
+    setDelimiterParsingDisabled(true);
+    map = new ConcurrentHashMap<String, Object>(mapToCopy);
+  }
+
+  public ConcurrentMapConfigurationExt(AbstractConfiguration config) {
+    super();
+    config.setDelimiterParsingDisabled(true);
+    for (Iterator<String> i = config.getKeys(); i.hasNext(); ) {
+      String name = i.next();
+      Object value = config.getProperty(name);
+      map.put(name, value);
+    }
+  }
+}
diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigUtil.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigUtil.java
index 564b699..8d81c23 100644
--- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigUtil.java
+++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigUtil.java
@@ -36,7 +36,7 @@
 import org.apache.commons.configuration.AbstractConfiguration;
 import org.apache.commons.configuration.Configuration;
 import org.apache.commons.configuration.EnvironmentConfiguration;
-import org.apache.commons.configuration.MapConfiguration;
+import org.apache.commons.configuration.PropertyConverter;
 import org.apache.commons.configuration.SystemConfiguration;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.reflect.FieldUtils;
@@ -55,10 +55,8 @@
 import com.netflix.config.ConcurrentCompositeConfiguration;
 import com.netflix.config.ConcurrentMapConfiguration;
 import com.netflix.config.ConfigurationManager;
-import com.netflix.config.DynamicConfiguration;
 import com.netflix.config.DynamicProperty;
 import com.netflix.config.DynamicPropertyFactory;
-import com.netflix.config.DynamicWatchedConfiguration;
 import com.netflix.config.WatchedUpdateListener;
 import com.netflix.config.WatchedUpdateResult;
 
@@ -103,16 +101,19 @@
     return null;
   }
 
-  public static List<String> getStringList(@Nonnull String key) {
-    return getStringList(ConfigurationManager.getConfigInstance(), key);
-  }
-
+  /**
+   * get comma separated list values from yaml string
+   */
   public static List<String> getStringList(@Nonnull Configuration config, @Nonnull String key) {
-    return config.getList(key).stream()
+    return parseArrayValue(config.getString(key)).stream()
         .map(v -> Objects.toString(v, null))
         .collect(Collectors.toList());
   }
 
+  public static List<String> parseArrayValue(String value) {
+    return PropertyConverter.split(value, ',', true);
+  }
+
   public static ConcurrentCompositeConfiguration createLocalConfig() {
     MicroserviceConfigLoader loader = new MicroserviceConfigLoader();
     loader.loadAndSort();
@@ -136,25 +137,25 @@
     ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration();
 
     duplicateCseConfigToServicecomb(config,
-        new ConcurrentMapConfiguration(new SystemConfiguration()),
+        new ConcurrentMapConfigurationExt(new SystemConfiguration()),
         "configFromSystem");
     duplicateCseConfigToServicecomb(config,
-        convertEnvVariable(new ConcurrentMapConfiguration(new EnvironmentConfiguration())),
+        convertEnvVariable(new ConcurrentMapConfigurationExt(new EnvironmentConfiguration())),
         "configFromEnvironment");
     // If there is extra configurations, add it into config.
     EXTRA_CONFIG_MAP.entrySet()
         .stream()
         .filter(mapEntry -> !mapEntry.getValue().isEmpty())
         .forEachOrdered(configMapEntry -> duplicateCseConfigToServicecomb(config,
-            new ConcurrentMapConfiguration(new MapConfiguration(configMapEntry.getValue())),
+            new ConcurrentMapConfigurationExt(configMapEntry.getValue()),
             configMapEntry.getKey()));
     // we have already copy the cse config to the serviceComb config when we load the config from local yaml files
     // hence, we do not need duplicate copy it.
-    config.addConfiguration(new DynamicConfiguration(
+    config.addConfiguration(new DynamicConfigurationExt(
             new MicroserviceConfigurationSource(configModelList), new NeverStartPollingScheduler()),
         "configFromYamlFile");
     duplicateCseConfigToServicecombAtFront(config,
-        new ConcurrentMapConfiguration(new MapConfiguration(ConfigMapping.getConvertedMap(config))),
+        new ConcurrentMapConfigurationExt(ConfigMapping.getConvertedMap(config)),
         "configFromMapping");
     return config;
   }
@@ -227,12 +228,12 @@
   private static void createDynamicWatchedConfiguration(
       ConcurrentCompositeConfiguration localConfiguration,
       ConfigCenterConfigurationSource configCenterConfigurationSource) {
-    ConcurrentMapConfiguration injectConfig = new ConcurrentMapConfiguration();
+    ConcurrentMapConfiguration injectConfig = new ConcurrentMapConfigurationExt();
     localConfiguration.addConfigurationAtFront(injectConfig, "extraInjectConfig");
     configCenterConfigurationSource.addUpdateListener(new ServiceCombPropertyUpdateListener(injectConfig));
 
-    DynamicWatchedConfiguration configFromConfigCenter =
-        new DynamicWatchedConfiguration(configCenterConfigurationSource);
+    DynamicWatchedConfigurationExt configFromConfigCenter =
+        new DynamicWatchedConfigurationExt(configCenterConfigurationSource);
     duplicateCseConfigToServicecomb(configFromConfigCenter);
     localConfiguration.addConfigurationAtFront(configFromConfigCenter, "configCenterConfig");
   }
diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicConfigurationExt.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicConfigurationExt.java
new file mode 100644
index 0000000..a89e89b
--- /dev/null
+++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicConfigurationExt.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.config;
+
+import com.netflix.config.AbstractPollingScheduler;
+import com.netflix.config.DynamicConfiguration;
+import com.netflix.config.PolledConfigurationSource;
+
+/**
+ * Disable delimiter parsing for string
+ */
+@SuppressWarnings("unchecked")
+public class DynamicConfigurationExt extends DynamicConfiguration {
+  public DynamicConfigurationExt(PolledConfigurationSource source, AbstractPollingScheduler scheduler) {
+    super();
+    setDelimiterParsingDisabled(true);
+    startPolling(source, scheduler);
+  }
+}
diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicWatchedConfigurationExt.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicWatchedConfigurationExt.java
new file mode 100644
index 0000000..3ea8a02
--- /dev/null
+++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicWatchedConfigurationExt.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.config;
+
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.netflix.config.ConcurrentMapConfiguration;
+import com.netflix.config.DynamicPropertyUpdater;
+import com.netflix.config.DynamicWatchedConfiguration;
+import com.netflix.config.WatchedConfigurationSource;
+import com.netflix.config.WatchedUpdateListener;
+import com.netflix.config.WatchedUpdateResult;
+
+/**
+ * Same as DynamicWatchedConfiguration but Disable delimiter parsing for string
+ *
+ * @see DynamicWatchedConfiguration
+ */
+@SuppressWarnings("unchecked")
+public class DynamicWatchedConfigurationExt extends ConcurrentMapConfiguration implements WatchedUpdateListener {
+
+  private final boolean ignoreDeletesFromSource;
+
+  private final DynamicPropertyUpdater updater;
+
+  private static final Logger LOGGER = LoggerFactory.getLogger(DynamicWatchedConfigurationExt.class);
+
+  private DynamicWatchedConfigurationExt(WatchedConfigurationSource source, boolean ignoreDeletesFromSource,
+      DynamicPropertyUpdater updater) {
+    this.ignoreDeletesFromSource = ignoreDeletesFromSource;
+    this.updater = updater;
+
+    setDelimiterParsingDisabled(true);
+
+    // get a current snapshot of the config source data
+    try {
+      Map<String, Object> currentData = source.getCurrentData();
+      WatchedUpdateResult result = WatchedUpdateResult.createFull(currentData);
+
+      updateConfiguration(result);
+    } catch (final Exception exc) {
+      LOGGER.error("could not getCurrentData() from the WatchedConfigurationSource", exc);
+    }
+
+    // add a listener for subsequent config updates
+    source.addUpdateListener(this);
+  }
+
+  public DynamicWatchedConfigurationExt(final WatchedConfigurationSource source) {
+    this(source, false, new DynamicPropertyUpdater());
+  }
+
+  @Override
+  public void updateConfiguration(final WatchedUpdateResult result) {
+    updater.updateProperties(result, this, ignoreDeletesFromSource);
+  }
+}
\ No newline at end of file
diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestConfigUtil.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestConfigUtil.java
index 2c5124c..e220bc6 100644
--- a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestConfigUtil.java
+++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestConfigUtil.java
@@ -31,6 +31,7 @@
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.commons.configuration.AbstractConfiguration;
+import org.apache.commons.configuration.Configuration;
 import org.apache.log4j.Level;
 import org.apache.log4j.Logger;
 import org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource;
@@ -88,6 +89,23 @@
   }
 
   @Test
+  public void testArrayData() {
+    Configuration configuration = ConfigUtil.createLocalConfig();
+    Assert.assertEquals("a,b,c", configuration.getString("test.commonSeparatedString"));
+    Assert.assertEquals(1, configuration.getStringArray("test.commonSeparatedString").length);
+    Assert.assertEquals("a,b,c", configuration.getStringArray("test.commonSeparatedString")[0]);
+
+    Assert.assertEquals("b,c,d", configuration.getString("test.commonSeparatedStringHolder"));
+    Assert.assertEquals(1, configuration.getStringArray("test.commonSeparatedStringHolder").length);
+    Assert.assertEquals("b,c,d", configuration.getStringArray("test.commonSeparatedStringHolder")[0]);
+
+    Assert.assertEquals("m", configuration.getString("test.stringArray")); // first element
+    Assert.assertEquals(2, configuration.getStringArray("test.stringArray").length);
+    Assert.assertEquals("m", configuration.getStringArray("test.stringArray")[0]);
+    Assert.assertEquals("n", configuration.getStringArray("test.stringArray")[1]);
+  }
+
+  @Test
   public void testAddConfig() {
     Map<String, Object> config = new HashMap<>();
     config.put("service_description.name", "service_name_test");
@@ -328,7 +346,7 @@
     Class<?>[] classes = Collections.class.getDeclaredClasses();
     Map<String, String> env = System.getenv();
     for (Class<?> cl : classes) {
-      if ("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
+      if ("java.util.Collections$UnmodifiableMap" .equals(cl.getName())) {
         Field field = cl.getDeclaredField("m");
         field.setAccessible(true);
         Object obj = field.get(env);
diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestYAMLUtil.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestYAMLUtil.java
index 7dd950c..ae63b9a 100644
--- a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestYAMLUtil.java
+++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestYAMLUtil.java
@@ -17,6 +17,8 @@
 
 package org.apache.servicecomb.config;
 
+import java.util.Map;
+
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -63,4 +65,13 @@
         + "name: hello", Object.class);
     Assert.assertEquals("hello", ((UnsafePerson) object).getName());
   }
+
+  @Test
+  @SuppressWarnings("unchecked")
+  public void testListValue() {
+    Map<String, Object> result = YAMLUtil.yaml2Properties("hello: a,b");
+    Assert.assertEquals(result.size(), 1);
+    String listValue = (String) result.get("hello");
+    Assert.assertEquals(listValue, "a,b");
+  }
 }
diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/archaius/sources/TestYAMLConfigurationSource.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/archaius/sources/TestYAMLConfigurationSource.java
index dee754d..f04aa7d 100644
--- a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/archaius/sources/TestYAMLConfigurationSource.java
+++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/archaius/sources/TestYAMLConfigurationSource.java
@@ -46,7 +46,7 @@
     PollResult result = configSource.poll(true, null);
     Map<String, Object> configMap = result.getComplete();
     assertNotNull(configMap);
-    assertEquals(25, configMap.size());
+    assertEquals(29, configMap.size());
     assertNotNull(configMap.get("trace.handler.sampler.percent"));
     assertEquals(0.5, configMap.get("trace.handler.sampler.percent"));
   }
@@ -63,7 +63,7 @@
 
     assertEquals(3, configSource.getConfigModels().size());
     assertNotNull(configMap);
-    assertEquals(36, configMap.size());
+    assertEquals(40, configMap.size());
     assertNotNull(configMap.get("trace.handler.sampler.percent"));
     assertEquals(0.5, configMap.get("trace.handler.sampler.percent"));
 
diff --git a/foundations/foundation-config/src/test/resources/microservice.yaml b/foundations/foundation-config/src/test/resources/microservice.yaml
index a7b1237..d820050 100644
--- a/foundations/foundation-config/src/test/resources/microservice.yaml
+++ b/foundations/foundation-config/src/test/resources/microservice.yaml
@@ -65,3 +65,11 @@
   cse:
     servicecomb:
       file: value # this is a config for TestConfigUtil.testCreateLocalConfigWithExtraConfig()
+
+holder: b,c,d
+test:
+  commonSeparatedString: a,b,c
+  commonSeparatedStringHolder: ${holder}
+  stringArray:
+    - m
+    - n
\ No newline at end of file
diff --git a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MonitorDefaultDeploymentProvider.java b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MonitorDefaultDeploymentProvider.java
index 7b42567..35a5ff2 100644
--- a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MonitorDefaultDeploymentProvider.java
+++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MonitorDefaultDeploymentProvider.java
@@ -17,15 +17,15 @@
 
 package org.apache.servicecomb.huaweicloud.dashboard.monitor;
 
-import com.google.common.annotations.VisibleForTesting;
+import java.util.List;
+
 import org.apache.commons.configuration.AbstractConfiguration;
-import org.apache.commons.lang3.ArrayUtils;
 import org.apache.servicecomb.config.ConfigUtil;
 import org.apache.servicecomb.deployment.DeploymentProvider;
 import org.apache.servicecomb.deployment.SystemBootstrapInfo;
 import org.apache.servicecomb.huaweicloud.dashboard.monitor.data.MonitorConstant;
 
-import java.util.Arrays;
+import com.google.common.annotations.VisibleForTesting;
 
 public class MonitorDefaultDeploymentProvider implements DeploymentProvider {
   private static AbstractConfiguration configuration = ConfigUtil.createLocalConfig();
@@ -40,12 +40,12 @@
     if (!systemKey.equals(MonitorConstant.SYSTEM_KEY_DASHBOARD_SERVICE)) {
       return null;
     }
-    String[] msAddresses = configuration.getStringArray(MonitorConstant.MONITOR_URI);
-    if (ArrayUtils.isEmpty(msAddresses)) {
+    List<String> msAddresses = ConfigUtil.parseArrayValue(configuration.getString(MonitorConstant.MONITOR_URI));
+    if (msAddresses.isEmpty()) {
       return null;
     }
     SystemBootstrapInfo ms = new SystemBootstrapInfo();
-    ms.setAccessURL(Arrays.asList(msAddresses));
+    ms.setAccessURL(msAddresses);
     return ms;
   }
 
diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/collect/ServiceCenterDefaultDeploymentProvider.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/collect/ServiceCenterDefaultDeploymentProvider.java
index ed06281..685f083 100644
--- a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/collect/ServiceCenterDefaultDeploymentProvider.java
+++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/collect/ServiceCenterDefaultDeploymentProvider.java
@@ -17,14 +17,15 @@
 
 package org.apache.servicecomb.serviceregistry.collect;
 
-import com.google.common.annotations.VisibleForTesting;
+import java.util.Arrays;
+import java.util.List;
+
 import org.apache.commons.configuration.AbstractConfiguration;
-import org.apache.commons.lang3.ArrayUtils;
 import org.apache.servicecomb.config.ConfigUtil;
 import org.apache.servicecomb.deployment.DeploymentProvider;
 import org.apache.servicecomb.deployment.SystemBootstrapInfo;
 
-import java.util.Arrays;
+import com.google.common.annotations.VisibleForTesting;
 
 public class ServiceCenterDefaultDeploymentProvider implements DeploymentProvider {
   public static final String SYSTEM_KEY_SERVICE_CENTER = "ServiceCenter";
@@ -37,11 +38,11 @@
       return null;
     }
     SystemBootstrapInfo sc = new SystemBootstrapInfo();
-    String[] urls = configuration.getStringArray("servicecomb.service.registry.address");
-    if (ArrayUtils.isEmpty(urls)) {
-      urls = new String[]{"http://127.0.0.1:30100"};
+    List<String> urls = ConfigUtil.parseArrayValue(configuration.getString("servicecomb.service.registry.address"));
+    if (urls.isEmpty()) {
+      urls = Arrays.asList("http://127.0.0.1:30100");
     }
-    sc.setAccessURL(Arrays.asList(urls));
+    sc.setAccessURL(urls);
     return sc;
   }