diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/boot/ApolloApplicationContextInitializer.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/boot/ApolloApplicationContextInitializer.java
index 908792b30d9..3959b1ef962 100644
--- a/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/boot/ApolloApplicationContextInitializer.java
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/boot/ApolloApplicationContextInitializer.java
@@ -11,6 +11,8 @@
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.CompositePropertySource;
@@ -37,9 +39,22 @@
* # will inject 'application' and 'FX.apollo' namespaces in bootstrap phase
* apollo.bootstrap.namespaces = application,FX.apollo
*
+ *
+ *
+ * If you want to load Apollo configurations even before Logging System Initialization Phase,
+ * add
+ *
+ * # set apollo.bootstrap.eagerLoad.enabled
+ * apollo.bootstrap.eagerLoad.enabled = true
+ *
+ *
+ * This would be very helpful when your logging configurations is set by Apollo.
+ *
+ * for example, you have defined logback-spring.xml in your project, and you want to inject some attributes into logback-spring.xml.
+ *
*/
public class ApolloApplicationContextInitializer implements
- ApplicationContextInitializer {
+ ApplicationContextInitializer , EnvironmentPostProcessor {
private static final Logger logger = LoggerFactory.getLogger(ApolloApplicationContextInitializer.class);
private static final Splitter NAMESPACE_SPLITTER = Splitter.on(",").omitEmptyStrings().trimResults();
private static final String[] APOLLO_SYSTEM_PROPERTIES = {"app.id", ConfigConsts.APOLLO_CLUSTER_KEY,
@@ -52,8 +67,6 @@ public class ApolloApplicationContextInitializer implements
public void initialize(ConfigurableApplicationContext context) {
ConfigurableEnvironment environment = context.getEnvironment();
- initializeSystemProperty(environment);
-
String enabled = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, "false");
if (!Boolean.valueOf(enabled)) {
logger.debug("Apollo bootstrap config is not enabled for context {}, see property: ${{}}", context, PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED);
@@ -61,6 +74,17 @@ public void initialize(ConfigurableApplicationContext context) {
}
logger.debug("Apollo bootstrap config is enabled for context {}", context);
+ initialize(environment);
+ }
+
+
+ /**
+ * Initialize Apollo Configurations Just after environment is ready.
+ *
+ * @param environment
+ */
+ protected void initialize(ConfigurableEnvironment environment) {
+
if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
//already initialized
return;
@@ -102,4 +126,37 @@ private void fillSystemPropertyFromEnvironment(ConfigurableEnvironment environme
System.setProperty(propertyName, propertyValue);
}
+
+ /**
+ *
+ * In order to load Apollo configurations as early as even before Spring loading logging system phase,
+ * this EnvironmentPostProcessor can be called Just After ConfigFileApplicationListener has succeeded.
+ *
+ *
+ * The processing sequence would be like this:
+ * Load Bootstrap properties and application properties -----> load Apollo configuration properties ----> Initialize Logging systems
+ *
+ * @param configurableEnvironment
+ * @param springApplication
+ */
+ @Override
+ public void postProcessEnvironment(ConfigurableEnvironment configurableEnvironment, SpringApplication springApplication) {
+
+ // should always initialize system properties like app.id in the first place
+ initializeSystemProperty(configurableEnvironment);
+
+ Boolean eagerLoadEnabled = configurableEnvironment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_EAGER_LOAD_ENABLED, Boolean.class, false);
+
+ //EnvironmentPostProcessor should not be triggered if you don't want Apollo Loading before Logging System Initialization
+ if (!eagerLoadEnabled) {
+ return;
+ }
+
+ Boolean bootstrapEnabled = configurableEnvironment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, Boolean.class, false);
+
+ if (bootstrapEnabled) {
+ initialize(configurableEnvironment);
+ }
+
+ }
}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/config/PropertySourcesConstants.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/config/PropertySourcesConstants.java
index 37b75d28098..b402e06dfea 100644
--- a/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/config/PropertySourcesConstants.java
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/config/PropertySourcesConstants.java
@@ -4,5 +4,6 @@ public interface PropertySourcesConstants {
String APOLLO_PROPERTY_SOURCE_NAME = "ApolloPropertySources";
String APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME = "ApolloBootstrapPropertySources";
String APOLLO_BOOTSTRAP_ENABLED = "apollo.bootstrap.enabled";
+ String APOLLO_BOOTSTRAP_EAGER_LOAD_ENABLED = "apollo.bootstrap.eagerLoad.enabled";
String APOLLO_BOOTSTRAP_NAMESPACES = "apollo.bootstrap.namespaces";
}
diff --git a/apollo-client/src/main/resources/META-INF/spring.factories b/apollo-client/src/main/resources/META-INF/spring.factories
index a4b7944a8d1..80c3b509bd3 100644
--- a/apollo-client/src/main/resources/META-INF/spring.factories
+++ b/apollo-client/src/main/resources/META-INF/spring.factories
@@ -2,3 +2,5 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ctrip.framework.apollo.spring.boot.ApolloAutoConfiguration
org.springframework.context.ApplicationContextInitializer=\
com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer
+org.springframework.boot.env.EnvironmentPostProcessor=\
+com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/BootstrapConfigTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/BootstrapConfigTest.java
index 13961a1959a..d223c3df229 100644
--- a/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/BootstrapConfigTest.java
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/BootstrapConfigTest.java
@@ -1,14 +1,12 @@
package com.ctrip.framework.apollo.spring;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfig;
+import com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer;
import com.ctrip.framework.apollo.spring.config.PropertySourcesConstants;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
import com.google.common.collect.Sets;
import org.junit.AfterClass;
import org.junit.Assert;
@@ -20,12 +18,21 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import java.util.List;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
/**
* @author Jason Song(song_s@ctrip.com)
*/
@@ -265,6 +272,48 @@ public void test() throws Exception {
}
}
+
+ @RunWith(SpringJUnit4ClassRunner.class)
+ @SpringBootTest(classes = ConfigurationWithoutConditionalOnProperty.class)
+ @DirtiesContext
+ public static class TestWithBootstrapEnabledAndEagerLoadEnabled extends
+ AbstractSpringIntegrationTest {
+
+ @BeforeClass
+ public static void beforeClass() {
+ doSetUp();
+
+ System.setProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, "true");
+ System.setProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_EAGER_LOAD_ENABLED, "true");
+
+ Config config = mock(Config.class);
+
+ mockConfig(ConfigConsts.NAMESPACE_APPLICATION, config);
+ }
+
+ @AfterClass
+ public static void afterClass() {
+ System.clearProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED);
+ System.clearProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_EAGER_LOAD_ENABLED);
+
+ doTearDown();
+ }
+
+ @Test
+ public void test() {
+ List processorList = SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());
+
+ Boolean containsApollo = !Collections2.filter(processorList, new Predicate() {
+ @Override
+ public boolean apply(EnvironmentPostProcessor input) {
+ return input instanceof ApolloApplicationContextInitializer;
+ }
+ }).isEmpty();
+ Assert.assertTrue(containsApollo);
+ }
+ }
+
+
@EnableAutoConfiguration
@Configuration
static class ConfigurationWithoutConditionalOnProperty {