Repository: ctripcorp/apollo
Pull request #2313 (2019-06-05 02:51:44)

client's SPI for customizing spring processors' loading
exposing 2 SPIs, ConfigPropertySourcesProcessorHelper and ConfigPropertySourcesProcessorHelper, for customizing spring processors's loading. So users can wrap their own client, and easily add their custom spring processors without modifying apollo-client code.

通过SPI 的方式自定义 spring processor 的加载过程 ，方便用户封装自己的客户端时添加自定义的processor，而不用去修改apollo-client中processor加载的代码。

codecov-io: # [Codecov](https://codecov.io/gh/ctripcorp/apollo/pull/2313?src=pr&el=h1) Report
> Merging [#2313](https://codecov.io/gh/ctripcorp/apollo/pull/2313?src=pr&el=desc) into [master](https://codecov.io/gh/ctripcorp/apollo/commit/89c370e114adfee766aba2a84c58eabff7272449?src=pr&el=desc) will **increase** coverage by `0.06%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/ctripcorp/apollo/pull/2313/graphs/tree.svg?width=650&token=2USkCxREbZ&height=150&src=pr)](https://codecov.io/gh/ctripcorp/apollo/pull/2313?src=pr&el=tree)

```diff
@@             Coverage Diff              @@
##             master    #2313      +/-   ##
============================================
+ Coverage     49.99%   50.05%   +0.06%     
- Complexity     1981     1991      +10     
============================================
  Files           402      404       +2     
  Lines         12400    12420      +20     
  Branches       1273     1274       +1     
============================================
+ Hits           6199     6217      +18     
- Misses         5758     5760       +2     
  Partials        443      443
```


| [Impacted Files](https://codecov.io/gh/ctripcorp/apollo/pull/2313?src=pr&el=tree) | Coverage Δ | Complexity Δ | |
|---|---|---|---|
| [...i/DefaultConfigPropertySourcesProcessorHelper.java](https://codecov.io/gh/ctripcorp/apollo/pull/2313/diff?src=pr&el=tree#diff-YXBvbGxvLWNsaWVudC9zcmMvbWFpbi9qYXZhL2NvbS9jdHJpcC9mcmFtZXdvcmsvYXBvbGxvL3NwcmluZy9zcGkvRGVmYXVsdENvbmZpZ1Byb3BlcnR5U291cmNlc1Byb2Nlc3NvckhlbHBlci5qYXZh) | `100% <100%> (ø)` | `4 <4> (?)` | |
| [.../spring/config/ConfigPropertySourcesProcessor.java](https://codecov.io/gh/ctripcorp/apollo/pull/2313/diff?src=pr&el=tree#diff-YXBvbGxvLWNsaWVudC9zcmMvbWFpbi9qYXZhL2NvbS9jdHJpcC9mcmFtZXdvcmsvYXBvbGxvL3NwcmluZy9jb25maWcvQ29uZmlnUHJvcGVydHlTb3VyY2VzUHJvY2Vzc29yLmphdmE=) | `100% <100%> (ø)` | `2 <1> (-1)` | :arrow_down: |
| [...amework/foundation/internals/ServiceBootstrap.java](https://codecov.io/gh/ctripcorp/apollo/pull/2313/diff?src=pr&el=tree#diff-YXBvbGxvLWNvcmUvc3JjL21haW4vamF2YS9jb20vY3RyaXAvZnJhbWV3b3JrL2ZvdW5kYXRpb24vaW50ZXJuYWxzL1NlcnZpY2VCb290c3RyYXAuamF2YQ==) | `94.44% <100%> (+6.94%)` | `6 <3> (+3)` | :arrow_up: |
| [...pollo/spring/annotation/ApolloConfigRegistrar.java](https://codecov.io/gh/ctripcorp/apollo/pull/2313/diff?src=pr&el=tree#diff-YXBvbGxvLWNsaWVudC9zcmMvbWFpbi9qYXZhL2NvbS9jdHJpcC9mcmFtZXdvcmsvYXBvbGxvL3NwcmluZy9hbm5vdGF0aW9uL0Fwb2xsb0NvbmZpZ1JlZ2lzdHJhci5qYXZh) | `100% <100%> (ø)` | `2 <1> (ø)` | :arrow_down: |
| [...spring/spi/DefaultApolloConfigRegistrarHelper.java](https://codecov.io/gh/ctripcorp/apollo/pull/2313/diff?src=pr&el=tree#diff-YXBvbGxvLWNsaWVudC9zcmMvbWFpbi9qYXZhL2NvbS9jdHJpcC9mcmFtZXdvcmsvYXBvbGxvL3NwcmluZy9zcGkvRGVmYXVsdEFwb2xsb0NvbmZpZ1JlZ2lzdHJhckhlbHBlci5qYXZh) | `100% <100%> (ø)` | `3 <3> (?)` | |
| [...mework/apollo/portal/component/PortalSettings.java](https://codecov.io/gh/ctripcorp/apollo/pull/2313/diff?src=pr&el=tree#diff-YXBvbGxvLXBvcnRhbC9zcmMvbWFpbi9qYXZhL2NvbS9jdHJpcC9mcmFtZXdvcmsvYXBvbGxvL3BvcnRhbC9jb21wb25lbnQvUG9ydGFsU2V0dGluZ3MuamF2YQ==) | `65.07% <0%> (-4.77%)` | `5% <0%> (ø)` | |
| [.../apollo/internals/RemoteConfigLongPollService.java](https://codecov.io/gh/ctripcorp/apollo/pull/2313/diff?src=pr&el=tree#diff-YXBvbGxvLWNsaWVudC9zcmMvbWFpbi9qYXZhL2NvbS9jdHJpcC9mcmFtZXdvcmsvYXBvbGxvL2ludGVybmFscy9SZW1vdGVDb25maWdMb25nUG9sbFNlcnZpY2UuamF2YQ==) | `80.24% <0%> (+0.61%)` | `28% <0%> (+1%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/ctripcorp/apollo/pull/2313?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/ctripcorp/apollo/pull/2313?src=pr&el=footer). Last update [89c370e...6ace144](https://codecov.io/gh/ctripcorp/apollo/pull/2313?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

coveralls: 
[![Coverage Status](https://coveralls.io/builds/23797094/badge)](https://coveralls.io/builds/23797094)

Coverage increased (+0.06%) to 53.623% when pulling **6ace1440e578363db8a41f7fb43c7a4e2d4eb85b on liucl:client_spring_extend_point** into **89c370e114adfee766aba2a84c58eabff7272449 on ctripcorp:master**.


Patch:
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/annotation/ApolloConfigRegistrar.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/annotation/ApolloConfigRegistrar.java
index 78430a5f6..562866d7a 100644
--- a/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/annotation/ApolloConfigRegistrar.java
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/annotation/ApolloConfigRegistrar.java
@@ -1,47 +1,20 @@
 package com.ctrip.framework.apollo.spring.annotation;
 
-import com.ctrip.framework.apollo.spring.property.SpringValueDefinitionProcessor;
-import java.util.HashMap;
-import java.util.Map;
+import com.ctrip.framework.apollo.spring.spi.ApolloConfigRegistrarHelper;
+import com.ctrip.framework.foundation.internals.ServiceBootstrap;
 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
 import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
-import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
-import org.springframework.core.annotation.AnnotationAttributes;
 import org.springframework.core.type.AnnotationMetadata;
 
-import com.ctrip.framework.apollo.spring.config.PropertySourcesProcessor;
-import com.ctrip.framework.apollo.spring.util.BeanRegistrationUtil;
-import com.google.common.collect.Lists;
-
 /**
  * @author Jason Song(song_s@ctrip.com)
  */
 public class ApolloConfigRegistrar implements ImportBeanDefinitionRegistrar {
-  @Override
-  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
-    AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata
-        .getAnnotationAttributes(EnableApolloConfig.class.getName()));
-    String[] namespaces = attributes.getStringArray("value");
-    int order = attributes.getNumber("order");
-    PropertySourcesProcessor.addNamespaces(Lists.newArrayList(namespaces), order);
-
-    Map<String, Object> propertySourcesPlaceholderPropertyValues = new HashMap<>();
-    // to make sure the default PropertySourcesPlaceholderConfigurer's priority is higher than PropertyPlaceholderConfigurer
-    propertySourcesPlaceholderPropertyValues.put("order", 0);
-
-    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesPlaceholderConfigurer.class.getName(),
-        PropertySourcesPlaceholderConfigurer.class, propertySourcesPlaceholderPropertyValues);
 
-    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesProcessor.class.getName(),
-        PropertySourcesProcessor.class);
+  private ApolloConfigRegistrarHelper helper = ServiceBootstrap.loadPrimary(ApolloConfigRegistrarHelper.class);
 
-    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloAnnotationProcessor.class.getName(),
-        ApolloAnnotationProcessor.class);
-
-    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueProcessor.class.getName(), SpringValueProcessor.class);
-    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueDefinitionProcessor.class.getName(), SpringValueDefinitionProcessor.class);
-
-    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloJsonValueProcessor.class.getName(),
-            ApolloJsonValueProcessor.class);
+  @Override
+  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
+    helper.registerBeanDefinitions(importingClassMetadata, registry);
   }
 }
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/config/ConfigPropertySourcesProcessor.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/config/ConfigPropertySourcesProcessor.java
index 9ab211127..d997d352e 100644
--- a/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/config/ConfigPropertySourcesProcessor.java
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/config/ConfigPropertySourcesProcessor.java
@@ -1,17 +1,10 @@
 package com.ctrip.framework.apollo.spring.config;
 
-import com.ctrip.framework.apollo.spring.annotation.SpringValueProcessor;
-import com.ctrip.framework.apollo.spring.property.SpringValueDefinitionProcessor;
-import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValueProcessor;
-import java.util.HashMap;
-import java.util.Map;
+import com.ctrip.framework.apollo.spring.spi.ConfigPropertySourcesProcessorHelper;
+import com.ctrip.framework.foundation.internals.ServiceBootstrap;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
 import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
-import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
-
-import com.ctrip.framework.apollo.spring.annotation.ApolloAnnotationProcessor;
-import com.ctrip.framework.apollo.spring.util.BeanRegistrationUtil;
 
 /**
  * Apollo Property Sources processor for Spring XML Based Application
@@ -21,31 +14,10 @@ import com.ctrip.framework.apollo.spring.util.BeanRegistrationUtil;
 public class ConfigPropertySourcesProcessor extends PropertySourcesProcessor
     implements BeanDefinitionRegistryPostProcessor {
 
+  private ConfigPropertySourcesProcessorHelper helper = ServiceBootstrap.loadPrimary(ConfigPropertySourcesProcessorHelper.class);
+
   @Override
   public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
-    Map<String, Object> propertySourcesPlaceholderPropertyValues = new HashMap<>();
-    // to make sure the default PropertySourcesPlaceholderConfigurer's priority is higher than PropertyPlaceholderConfigurer
-    propertySourcesPlaceholderPropertyValues.put("order", 0);
-
-    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesPlaceholderConfigurer.class.getName(),
-        PropertySourcesPlaceholderConfigurer.class, propertySourcesPlaceholderPropertyValues);
-    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloAnnotationProcessor.class.getName(),
-        ApolloAnnotationProcessor.class);
-    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueProcessor.class.getName(), SpringValueProcessor.class);
-    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloJsonValueProcessor.class.getName(),
-        ApolloJsonValueProcessor.class);
-
-    processSpringValueDefinition(registry);
-  }
-
-  /**
-   * For Spring 3.x versions, the BeanDefinitionRegistryPostProcessor would not be
-   * instantiated if it is added in postProcessBeanDefinitionRegistry phase, so we have to manually
-   * call the postProcessBeanDefinitionRegistry method of SpringValueDefinitionProcessor here...
-   */
-  private void processSpringValueDefinition(BeanDefinitionRegistry registry) {
-    SpringValueDefinitionProcessor springValueDefinitionProcessor = new SpringValueDefinitionProcessor();
-
-    springValueDefinitionProcessor.postProcessBeanDefinitionRegistry(registry);
+    helper.postProcessBeanDefinitionRegistry(registry);
   }
 }
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/spi/ApolloConfigRegistrarHelper.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/spi/ApolloConfigRegistrarHelper.java
new file mode 100644
index 000000000..124420193
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/spi/ApolloConfigRegistrarHelper.java
@@ -0,0 +1,10 @@
+package com.ctrip.framework.apollo.spring.spi;
+
+import com.ctrip.framework.apollo.core.spi.Ordered;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.core.type.AnnotationMetadata;
+
+public interface ApolloConfigRegistrarHelper extends Ordered {
+
+  void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/spi/ConfigPropertySourcesProcessorHelper.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/spi/ConfigPropertySourcesProcessorHelper.java
new file mode 100644
index 000000000..939e3da51
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/spi/ConfigPropertySourcesProcessorHelper.java
@@ -0,0 +1,10 @@
+package com.ctrip.framework.apollo.spring.spi;
+
+import com.ctrip.framework.apollo.core.spi.Ordered;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+
+public interface ConfigPropertySourcesProcessorHelper extends Ordered {
+
+  void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/spi/DefaultApolloConfigRegistrarHelper.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/spi/DefaultApolloConfigRegistrarHelper.java
new file mode 100644
index 000000000..e25f4a88c
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/spi/DefaultApolloConfigRegistrarHelper.java
@@ -0,0 +1,51 @@
+package com.ctrip.framework.apollo.spring.spi;
+
+import com.ctrip.framework.apollo.core.spi.Ordered;
+import com.ctrip.framework.apollo.spring.annotation.ApolloAnnotationProcessor;
+import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValueProcessor;
+import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
+import com.ctrip.framework.apollo.spring.annotation.SpringValueProcessor;
+import com.ctrip.framework.apollo.spring.config.PropertySourcesProcessor;
+import com.ctrip.framework.apollo.spring.property.SpringValueDefinitionProcessor;
+import com.ctrip.framework.apollo.spring.util.BeanRegistrationUtil;
+import com.google.common.collect.Lists;
+import java.util.HashMap;
+import java.util.Map;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
+import org.springframework.core.annotation.AnnotationAttributes;
+import org.springframework.core.type.AnnotationMetadata;
+
+public class DefaultApolloConfigRegistrarHelper implements ApolloConfigRegistrarHelper {
+
+  @Override
+  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
+    AnnotationAttributes attributes = AnnotationAttributes
+        .fromMap(importingClassMetadata.getAnnotationAttributes(EnableApolloConfig.class.getName()));
+    String[] namespaces = attributes.getStringArray("value");
+    int order = attributes.getNumber("order");
+    PropertySourcesProcessor.addNamespaces(Lists.newArrayList(namespaces), order);
+
+    Map<String, Object> propertySourcesPlaceholderPropertyValues = new HashMap<>();
+    // to make sure the default PropertySourcesPlaceholderConfigurer's priority is higher than PropertyPlaceholderConfigurer
+    propertySourcesPlaceholderPropertyValues.put("order", 0);
+
+    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesPlaceholderConfigurer.class.getName(),
+        PropertySourcesPlaceholderConfigurer.class, propertySourcesPlaceholderPropertyValues);
+    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesProcessor.class.getName(),
+        PropertySourcesProcessor.class);
+    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloAnnotationProcessor.class.getName(),
+        ApolloAnnotationProcessor.class);
+    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueProcessor.class.getName(),
+        SpringValueProcessor.class);
+    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueDefinitionProcessor.class.getName(),
+        SpringValueDefinitionProcessor.class);
+    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloJsonValueProcessor.class.getName(),
+        ApolloJsonValueProcessor.class);
+  }
+
+  @Override
+  public int getOrder() {
+    return Ordered.LOWEST_PRECEDENCE;
+  }
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/spi/DefaultConfigPropertySourcesProcessorHelper.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/spi/DefaultConfigPropertySourcesProcessorHelper.java
new file mode 100644
index 000000000..5e95c99e8
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/spi/DefaultConfigPropertySourcesProcessorHelper.java
@@ -0,0 +1,50 @@
+package com.ctrip.framework.apollo.spring.spi;
+
+import com.ctrip.framework.apollo.core.spi.Ordered;
+import com.ctrip.framework.apollo.spring.annotation.ApolloAnnotationProcessor;
+import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValueProcessor;
+import com.ctrip.framework.apollo.spring.annotation.SpringValueProcessor;
+import com.ctrip.framework.apollo.spring.property.SpringValueDefinitionProcessor;
+import com.ctrip.framework.apollo.spring.util.BeanRegistrationUtil;
+import java.util.HashMap;
+import java.util.Map;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
+
+public class DefaultConfigPropertySourcesProcessorHelper implements ConfigPropertySourcesProcessorHelper {
+
+  @Override
+  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
+    Map<String, Object> propertySourcesPlaceholderPropertyValues = new HashMap<>();
+    // to make sure the default PropertySourcesPlaceholderConfigurer's priority is higher than PropertyPlaceholderConfigurer
+    propertySourcesPlaceholderPropertyValues.put("order", 0);
+
+    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesPlaceholderConfigurer.class.getName(),
+        PropertySourcesPlaceholderConfigurer.class, propertySourcesPlaceholderPropertyValues);
+    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloAnnotationProcessor.class.getName(),
+        ApolloAnnotationProcessor.class);
+    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueProcessor.class.getName(),
+        SpringValueProcessor.class);
+    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloJsonValueProcessor.class.getName(),
+        ApolloJsonValueProcessor.class);
+
+    processSpringValueDefinition(registry);
+  }
+
+  /**
+   * For Spring 3.x versions, the BeanDefinitionRegistryPostProcessor would not be instantiated if
+   * it is added in postProcessBeanDefinitionRegistry phase, so we have to manually call the
+   * postProcessBeanDefinitionRegistry method of SpringValueDefinitionProcessor here...
+   */
+  private void processSpringValueDefinition(BeanDefinitionRegistry registry) {
+    SpringValueDefinitionProcessor springValueDefinitionProcessor = new SpringValueDefinitionProcessor();
+
+    springValueDefinitionProcessor.postProcessBeanDefinitionRegistry(registry);
+  }
+
+  @Override
+  public int getOrder() {
+    return Ordered.LOWEST_PRECEDENCE;
+  }
+}
diff --git a/apollo-client/src/main/resources/META-INF/services/com.ctrip.framework.apollo.spring.spi.ApolloConfigRegistrarHelper b/apollo-client/src/main/resources/META-INF/services/com.ctrip.framework.apollo.spring.spi.ApolloConfigRegistrarHelper
new file mode 100644
index 000000000..cb6e90084
--- /dev/null
+++ b/apollo-client/src/main/resources/META-INF/services/com.ctrip.framework.apollo.spring.spi.ApolloConfigRegistrarHelper
@@ -0,0 +1,1 @@
+com.ctrip.framework.apollo.spring.spi.DefaultApolloConfigRegistrarHelper
diff --git a/apollo-client/src/main/resources/META-INF/services/com.ctrip.framework.apollo.spring.spi.ConfigPropertySourcesProcessorHelper b/apollo-client/src/main/resources/META-INF/services/com.ctrip.framework.apollo.spring.spi.ConfigPropertySourcesProcessorHelper
new file mode 100644
index 000000000..07fd0df3c
--- /dev/null
+++ b/apollo-client/src/main/resources/META-INF/services/com.ctrip.framework.apollo.spring.spi.ConfigPropertySourcesProcessorHelper
@@ -0,0 +1,1 @@
+com.ctrip.framework.apollo.spring.spi.DefaultConfigPropertySourcesProcessorHelper
diff --git a/apollo-core/src/main/java/com/ctrip/framework/foundation/internals/ServiceBootstrap.java b/apollo-core/src/main/java/com/ctrip/framework/foundation/internals/ServiceBootstrap.java
index a34a518cf..4ae8bbba7 100644
--- a/apollo-core/src/main/java/com/ctrip/framework/foundation/internals/ServiceBootstrap.java
+++ b/apollo-core/src/main/java/com/ctrip/framework/foundation/internals/ServiceBootstrap.java
@@ -1,6 +1,11 @@
 package com.ctrip.framework.foundation.internals;
 
+import com.ctrip.framework.apollo.core.spi.Ordered;
+import com.google.common.collect.Lists;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.Iterator;
+import java.util.List;
 import java.util.ServiceLoader;
 
 public class ServiceBootstrap {
@@ -19,4 +24,31 @@ public class ServiceBootstrap {
 
     return loader.iterator();
   }
+
+  public static <S extends Ordered> List<S> loadAllOrdered(Class<S> clazz) {
+    Iterator<S> iterator = loadAll(clazz);
+
+    if (!iterator.hasNext()) {
+      throw new IllegalStateException(String.format(
+          "No implementation defined in /META-INF/services/%s, please check whether the file exists and has the right implementation class!",
+          clazz.getName()));
+    }
+
+    List<S> candidates = Lists.newArrayList(iterator);
+    Collections.sort(candidates, new Comparator<S>() {
+      @Override
+      public int compare(S o1, S o2) {
+        // the smaller order has higher priority
+        return Integer.compare(o1.getOrder(), o2.getOrder());
+      }
+    });
+
+    return candidates;
+  }
+
+  public static <S extends Ordered> S loadPrimary(Class<S> clazz) {
+    List<S> candidates = loadAllOrdered(clazz);
+
+    return candidates.get(0);
+  }
 }


Test patch:
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/spi/ApolloConfigRegistrarHelperTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/spi/ApolloConfigRegistrarHelperTest.java
new file mode 100644
index 000000000..1d7f099d2
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/spi/ApolloConfigRegistrarHelperTest.java
@@ -0,0 +1,22 @@
+package com.ctrip.framework.apollo.spring.spi;
+
+import static org.springframework.test.util.AssertionErrors.assertEquals;
+
+import com.ctrip.framework.apollo.spring.annotation.ApolloConfigRegistrar;
+import java.lang.reflect.Field;
+import org.junit.Test;
+import org.springframework.util.ReflectionUtils;
+
+public class ApolloConfigRegistrarHelperTest {
+
+  @Test
+  public void testHelperLoadingOrder() {
+    ApolloConfigRegistrar apolloConfigRegistrar = new ApolloConfigRegistrar();
+
+    Field field = ReflectionUtils.findField(ApolloConfigRegistrar.class, "helper");
+    ReflectionUtils.makeAccessible(field);
+    Object helper = ReflectionUtils.getField(field, apolloConfigRegistrar);
+
+    assertEquals("helper is not TestRegistrarHelper instance", TestRegistrarHelper.class, helper.getClass());
+  }
+}
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/spi/ConfigPropertySourcesProcessorHelperTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/spi/ConfigPropertySourcesProcessorHelperTest.java
new file mode 100644
index 000000000..fe4865268
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/spi/ConfigPropertySourcesProcessorHelperTest.java
@@ -0,0 +1,22 @@
+package com.ctrip.framework.apollo.spring.spi;
+
+import static org.springframework.test.util.AssertionErrors.assertEquals;
+
+import com.ctrip.framework.apollo.spring.config.ConfigPropertySourcesProcessor;
+import java.lang.reflect.Field;
+import org.junit.Test;
+import org.springframework.util.ReflectionUtils;
+
+public class ConfigPropertySourcesProcessorHelperTest {
+
+  @Test
+  public void testHelperLoadingOrder() {
+    ConfigPropertySourcesProcessor processor = new ConfigPropertySourcesProcessor();
+
+    Field field = ReflectionUtils.findField(ConfigPropertySourcesProcessor.class, "helper");
+    ReflectionUtils.makeAccessible(field);
+    Object helper = ReflectionUtils.getField(field, processor);
+
+    assertEquals("helper is not TestProcessorHelper instance", TestProcessorHelper.class, helper.getClass());
+  }
+}
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/spi/TestProcessorHelper.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/spi/TestProcessorHelper.java
new file mode 100644
index 000000000..a80952f58
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/spi/TestProcessorHelper.java
@@ -0,0 +1,18 @@
+package com.ctrip.framework.apollo.spring.spi;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+
+public class TestProcessorHelper extends DefaultConfigPropertySourcesProcessorHelper {
+
+  @Override
+  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
+      throws BeansException {
+    super.postProcessBeanDefinitionRegistry(registry);
+  }
+
+  @Override
+  public int getOrder() {
+    return 0;
+  }
+}
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/spi/TestRegistrarHelper.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/spi/TestRegistrarHelper.java
new file mode 100644
index 000000000..26285470a
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/spi/TestRegistrarHelper.java
@@ -0,0 +1,18 @@
+package com.ctrip.framework.apollo.spring.spi;
+
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.core.type.AnnotationMetadata;
+
+public class TestRegistrarHelper extends DefaultApolloConfigRegistrarHelper {
+
+  @Override
+  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
+      BeanDefinitionRegistry registry) {
+    super.registerBeanDefinitions(importingClassMetadata, registry);
+  }
+
+  @Override
+  public int getOrder() {
+    return 0;
+  }
+}
diff --git a/apollo-client/src/test/resources/META-INF/services/com.ctrip.framework.apollo.spring.spi.ApolloConfigRegistrarHelper b/apollo-client/src/test/resources/META-INF/services/com.ctrip.framework.apollo.spring.spi.ApolloConfigRegistrarHelper
new file mode 100644
index 000000000..b51082c31
--- /dev/null
+++ b/apollo-client/src/test/resources/META-INF/services/com.ctrip.framework.apollo.spring.spi.ApolloConfigRegistrarHelper
@@ -0,0 +1,1 @@
+com.ctrip.framework.apollo.spring.spi.TestRegistrarHelper
diff --git a/apollo-client/src/test/resources/META-INF/services/com.ctrip.framework.apollo.spring.spi.ConfigPropertySourcesProcessorHelper b/apollo-client/src/test/resources/META-INF/services/com.ctrip.framework.apollo.spring.spi.ConfigPropertySourcesProcessorHelper
new file mode 100644
index 000000000..64d7bb610
--- /dev/null
+++ b/apollo-client/src/test/resources/META-INF/services/com.ctrip.framework.apollo.spring.spi.ConfigPropertySourcesProcessorHelper
@@ -0,0 +1,1 @@
+com.ctrip.framework.apollo.spring.spi.TestProcessorHelper
diff --git a/apollo-core/src/test/java/com/ctrip/framework/foundation/internals/ServiceBootstrapTest.java b/apollo-core/src/test/java/com/ctrip/framework/foundation/internals/ServiceBootstrapTest.java
index b3b8ed617..620da7d0c 100644
--- a/apollo-core/src/test/java/com/ctrip/framework/foundation/internals/ServiceBootstrapTest.java
+++ b/apollo-core/src/test/java/com/ctrip/framework/foundation/internals/ServiceBootstrapTest.java
@@ -1,5 +1,6 @@
 package com.ctrip.framework.foundation.internals;
 
+import com.ctrip.framework.apollo.core.spi.Ordered;
 import org.junit.Test;
 
 import java.util.ServiceConfigurationError;
@@ -36,6 +37,17 @@ public class ServiceBootstrapTest {
     ServiceBootstrap.loadFirst(Interface5.class);
   }
 
+  @Test
+  public void loadPrimarySuccessfully() {
+    Interface6 service = ServiceBootstrap.loadPrimary(Interface6.class);
+    assertTrue(service instanceof Interface6Impl1);
+  }
+
+  @Test(expected = IllegalStateException.class)
+  public void loadPrimaryWithServiceFileButNoServiceImpl() {
+    ServiceBootstrap.loadPrimary(Interface7.class);
+  }
+
   private interface Interface1 {
   }
 
@@ -53,4 +65,24 @@ public class ServiceBootstrapTest {
 
   private interface Interface5 {
   }
+
+  private interface Interface6 extends Ordered {
+  }
+
+  public static class Interface6Impl1 implements Interface6 {
+    @Override
+    public int getOrder() {
+      return 1;
+    }
+  }
+
+  public static class Interface6Impl2 implements Interface6 {
+    @Override
+    public int getOrder() {
+      return 2;
+    }
+  }
+
+  private interface Interface7 extends Ordered {
+  }
 }
diff --git a/apollo-core/src/test/resources/META-INF/services/com.ctrip.framework.foundation.internals.ServiceBootstrapTest$Interface6 b/apollo-core/src/test/resources/META-INF/services/com.ctrip.framework.foundation.internals.ServiceBootstrapTest$Interface6
new file mode 100644
index 000000000..7041108b2
--- /dev/null
+++ b/apollo-core/src/test/resources/META-INF/services/com.ctrip.framework.foundation.internals.ServiceBootstrapTest$Interface6
@@ -0,0 +1,2 @@
+com.ctrip.framework.foundation.internals.ServiceBootstrapTest$Interface6Impl1
+com.ctrip.framework.foundation.internals.ServiceBootstrapTest$Interface6Impl2