diff --git a/apollo-boot-starter/README.md b/apollo-boot-starter/README.md deleted file mode 100644 index f31d020e20dbf34a5e99b4474a8fc96964aba421..0000000000000000000000000000000000000000 --- a/apollo-boot-starter/README.md +++ /dev/null @@ -1,40 +0,0 @@ -## Apollo Spring Boot Starter ## - -### 一、说明 ### -由于@EnableApolloConfig使用ImportBeanDefinitionRegistrar初始化PropertySource,时机晚于spring boot的其他PropertySource,使得@ConditionalOnProperty无法生效。这里使用SpringApplicationRunListener初始化Apollo的PropertySource,优先级如下所示: - -```text -StubPropertySource {name='servletConfigInitParams'} -StubPropertySource {name='servletContextInitParams'} -MapPropertySource {name='systemProperties'} -SystemEnvironmentPropertySource {name='systemEnvironment'} -CompositePropertySource [name='ApolloPropertySources', propertySources=[ConfigPropertySource {name='application'}]] -RandomValuePropertySource {name='random'} -PropertiesPropertySource {name='applicationConfig: [classpath:/application-local.properties]'} -PropertiesPropertySource {name='applicationConfig: [classpath:/application.properties]'} -MapPropertySource {name='refresh'} -``` - -### 二、Maven Dependency ### -```xml - - com.ctrip.framework.apollo - apollo-spring-boot-starter - 0.9.1 - - - - - com.ctrip.framework.apollo - apollo-client - 0.9.1-SNAPSHOT - -``` - -### 三、配置 ### -```properties -#默认为true -apollo.enabled=true -#默认为application -apollo.namespaces=application,FX.apollo -``` \ No newline at end of file diff --git a/apollo-boot-starter/pom.xml b/apollo-boot-starter/pom.xml deleted file mode 100644 index 6bc830686e879a5d16cbafbff6413498b6a8f133..0000000000000000000000000000000000000000 --- a/apollo-boot-starter/pom.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - 4.0.0 - com.ctrip.framework.apollo - apollo-spring-boot-starter - Apollo Spring Boot Starter - 0.9.1 - - 1.7 - UTF-8 - 1.5.9.RELEASE - - - - org.springframework.boot - spring-boot-autoconfigure - ${spring.boot.version} - - - - - com.ctrip.framework.apollo - apollo-client - 0.9.1-SNAPSHOT - provided - - - - - org.springframework.boot - spring-boot-starter-test - ${spring.boot.version} - test - - - commons-logging - commons-logging - 1.2 - - - ch.qos.logback - logback-core - 1.2.3 - - - ch.qos.logback - logback-classic - 1.2.3 - - - - - - - - org.apache.maven.plugins - maven-source-plugin - 2.2.1 - - true - - - - compile - - jar - - - - - - - diff --git a/apollo-boot-starter/src/main/java/com/ctrip/framework/apollo/boot/ApolloAutoConfiguration.java b/apollo-boot-starter/src/main/java/com/ctrip/framework/apollo/boot/ApolloAutoConfiguration.java deleted file mode 100644 index 86ad576f277c2ce18c562431273ea76d5da792c8..0000000000000000000000000000000000000000 --- a/apollo-boot-starter/src/main/java/com/ctrip/framework/apollo/boot/ApolloAutoConfiguration.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.ctrip.framework.apollo.boot; - -import com.ctrip.framework.apollo.spring.config.ConfigPropertySource; -import com.ctrip.framework.apollo.spring.config.ConfigPropertySourcesProcessor; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -@ConditionalOnClass(ConfigPropertySource.class) -@ConditionalOnProperty(prefix = "apollo", name = "enabled", matchIfMissing = true) -public class ApolloAutoConfiguration { - - @Bean - public ConfigPropertySourcesProcessor configPropertySourcesProcessor() { - return new ConfigPropertySourcesProcessor(); - } -} diff --git a/apollo-boot-starter/src/main/java/com/ctrip/framework/apollo/boot/ApolloSpringApplicationRunListener.java b/apollo-boot-starter/src/main/java/com/ctrip/framework/apollo/boot/ApolloSpringApplicationRunListener.java deleted file mode 100644 index c8c173f3b9d763e835bef819cad4c36f23d274e7..0000000000000000000000000000000000000000 --- a/apollo-boot-starter/src/main/java/com/ctrip/framework/apollo/boot/ApolloSpringApplicationRunListener.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.ctrip.framework.apollo.boot; - -import com.ctrip.framework.apollo.Config; -import com.ctrip.framework.apollo.ConfigService; -import com.ctrip.framework.apollo.spring.config.ConfigPropertySource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.SpringApplicationRunListener; -import org.springframework.boot.env.EnumerableCompositePropertySource; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.core.Ordered; -import org.springframework.core.PriorityOrdered; -import org.springframework.core.env.*; -import org.springframework.util.ReflectionUtils; - -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -public class ApolloSpringApplicationRunListener implements SpringApplicationRunListener, PriorityOrdered { - private static final String APOLLO_PROPERTY_SOURCE_NAME = "ApolloPropertySources"; - - private static Logger logger = LoggerFactory.getLogger(ApolloSpringApplicationRunListener.class); - - private SpringApplication application; - private String[] args; - - public ApolloSpringApplicationRunListener(SpringApplication application, String[] args) { - this.application = application; - this.args = args; - } - - public void starting() { - } - - public void environmentPrepared(ConfigurableEnvironment environment) { - } - - public void contextPrepared(ConfigurableApplicationContext context) { - } - - - public void contextLoaded(ConfigurableApplicationContext configurableApplicationContext) { - ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment(); - String enabled = environment.getProperty("apollo.enabled", "true"); - if (!"true".equals(enabled)) { - logger.warn("Apollo is not enabled. see property: ${apollo.enabled}"); - return; - } - - String namespaces = environment.getProperty("apollo.namespaces", "application"); - logger.info("Configured namespaces: {}", namespaces); - List namespaceList = Arrays.asList(namespaces.split(",")); - - MutablePropertySources propertySources = environment.getPropertySources(); - if (propertySources.contains(APOLLO_PROPERTY_SOURCE_NAME)) { - //already initialized - return; - } - CompositePropertySource composite = new CompositePropertySource(APOLLO_PROPERTY_SOURCE_NAME); - for (String namespace : namespaceList) { - Config config = ConfigService.getConfig(namespace); - - composite.addPropertySource(new ConfigPropertySource(namespace, config)); - } - - propertySources.addAfter(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, composite); - - StringBuilder logInfo = new StringBuilder(); - for (PropertySource propertySource : propertySources) { - if (propertySource.getClass().getSimpleName().contains("ConfigurationPropertySources")) { - //打印文件配置 - try { - Field field = propertySource.getClass().getDeclaredField("sources"); - field.setAccessible(true); - List list = (List) ReflectionUtils.getField(field, propertySource); - for (Object s : list) { - if (s instanceof EnumerableCompositePropertySource) { - EnumerableCompositePropertySource enumerableCompositePropertySource = (EnumerableCompositePropertySource) s; - - Collection> source = enumerableCompositePropertySource.getSource(); - for (PropertySource a : source) { - logInfo.append('\t').append(a.toString()).append("\n"); - } - } - } - } catch (NoSuchFieldException e) { - //do nothing - } - } else { - logInfo.append('\t').append(propertySource.toString()).append("\n"); - } - - } - logger.info("PropertySources piority:\n{}", logInfo.toString()); - } - - public void finished(ConfigurableApplicationContext configurableApplicationContext, Throwable throwable) { - - } - - public int getOrder() { - return Ordered.LOWEST_PRECEDENCE; - } -} diff --git a/apollo-boot-starter/src/main/resources/META-INF/spring.factories b/apollo-boot-starter/src/main/resources/META-INF/spring.factories deleted file mode 100644 index d87d99ba4a1d083a190cb60566e8550d5672669a..0000000000000000000000000000000000000000 --- a/apollo-boot-starter/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,4 +0,0 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -com.ctrip.framework.apollo.boot.ApolloAutoConfiguration -org.springframework.boot.SpringApplicationRunListener=\ -com.ctrip.framework.apollo.boot.ApolloSpringApplicationRunListener \ No newline at end of file diff --git a/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloBootStarterWithConditionalOnPropertyTests.java b/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloBootStarterWithConditionalOnPropertyTests.java deleted file mode 100644 index 80c92d05d35777eebd3953798864d298ddce8dce..0000000000000000000000000000000000000000 --- a/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloBootStarterWithConditionalOnPropertyTests.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.ctrip.framework.apollo.boot; - -import com.ctrip.framework.apollo.boot.bean.TestBean; - -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.util.Assert; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = {ApolloTestConfigurationWithConditionalOnProperty.class}) -public class ApolloBootStarterWithConditionalOnPropertyTests { - - @BeforeClass - public static void beforeClass() { - System.setProperty("app.id", "1"); // C:\opt\data\1\config-cache\1+default+application.properties文件内容:apollo.test.testBean=true - System.setProperty("spring.profiles.active", "local"); - System.setProperty("env", "local"); - } - - @Autowired - private TestBean testBean; - - @Test - public void testSuccess() { - Assert.notNull(testBean, "testBean is not null"); - Assert.isTrue(testBean.execute(), "testBean.execute success"); - } - -} diff --git a/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloBootStarterWithConditionalOnPropertyTests_Failed.java b/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloBootStarterWithConditionalOnPropertyTests_Failed.java deleted file mode 100644 index b39099c7433ff8e0050b9ec030d4c7ac752d3eaf..0000000000000000000000000000000000000000 --- a/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloBootStarterWithConditionalOnPropertyTests_Failed.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.ctrip.framework.apollo.boot; - -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.util.Assert; - -import com.ctrip.framework.apollo.boot.bean.TestBean; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = {ApolloTestConfigurationWithConditionalOnProperty.class}) -public class ApolloBootStarterWithConditionalOnPropertyTests_Failed { - - @BeforeClass - public static void beforeClass() { -// System.setProperty("app.id", "1"); app.id not set - System.setProperty("spring.profiles.active", "local"); - System.setProperty("env", "local"); - } - - @Autowired(required = false) - private TestBean testBean; - - @Test - public void testWithAppIdNotSet() { - Assert.isNull(testBean, "testBean is not null"); - } - -} diff --git a/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloBootStarterWithoutConditionalOnPropertyTests.java b/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloBootStarterWithoutConditionalOnPropertyTests.java deleted file mode 100644 index 01bba6a7f94c3119e39af977972be89050040b58..0000000000000000000000000000000000000000 --- a/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloBootStarterWithoutConditionalOnPropertyTests.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.ctrip.framework.apollo.boot; - -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.util.Assert; - -import com.ctrip.framework.apollo.boot.bean.TestBean; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = {ApolloTestConfigurationWithoutConditionalOnProperty.class}) -public class ApolloBootStarterWithoutConditionalOnPropertyTests { - - @BeforeClass - public static void beforeClass() { - System.setProperty("app.id", "1"); // C:\opt\data\1\config-cache\1+default+application.properties文件内容:apollo.test.testBean=true - System.setProperty("spring.profiles.active", "local"); - System.setProperty("env", "local"); - } - - @Autowired - private TestBean testBean; - - @Test - public void testSuccess() { - Assert.notNull(testBean, "testBean is not null"); - Assert.isTrue(testBean.execute(), "testBean.execute success"); - } - -} diff --git a/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloBootStarterWithoutConditionalOnPropertyTests_WithoutAppId.java b/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloBootStarterWithoutConditionalOnPropertyTests_WithoutAppId.java deleted file mode 100644 index 7e8e127929751191557517de1818ab8d9ef26d80..0000000000000000000000000000000000000000 --- a/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloBootStarterWithoutConditionalOnPropertyTests_WithoutAppId.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.ctrip.framework.apollo.boot; - -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.util.Assert; - -import com.ctrip.framework.apollo.boot.bean.TestBean; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = {ApolloTestConfigurationWithoutConditionalOnProperty.class}) -public class ApolloBootStarterWithoutConditionalOnPropertyTests_WithoutAppId { - - @BeforeClass - public static void beforeClass() { -// System.setProperty("app.id", "1"); app id not set - System.setProperty("spring.profiles.active", "local"); - System.setProperty("env", "local"); - } - - @Autowired - private TestBean testBean; - - @Test - public void testSuccess() { - Assert.notNull(testBean, "testBean is not null"); - Assert.isTrue(testBean.execute(), "testBean.execute success"); - } - -} diff --git a/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloTestApplication.java b/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloTestApplication.java deleted file mode 100644 index 9f209fb7cf7b4498177f9ce4043aeaecf8e84dc5..0000000000000000000000000000000000000000 --- a/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloTestApplication.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.ctrip.framework.apollo.boot; - - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.SpringBootConfiguration; - -@SpringBootConfiguration -public class ApolloTestApplication { - - public static void main(String[] args) { - SpringApplication.run(ApolloTestApplication.class, args); - } -} diff --git a/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloTestConfigurationWithConditionalOnProperty.java b/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloTestConfigurationWithConditionalOnProperty.java deleted file mode 100644 index 9a9f3fe036501ed1ce9e758f5df651bbed26e4ea..0000000000000000000000000000000000000000 --- a/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloTestConfigurationWithConditionalOnProperty.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.ctrip.framework.apollo.boot; - -import com.ctrip.framework.apollo.boot.bean.TestBean; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class ApolloTestConfigurationWithConditionalOnProperty { - - @Bean - @ConditionalOnProperty(prefix = "apollo.test", name = "testBean") - public TestBean testBean() { - return new TestBean(); - } -} diff --git a/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloTestConfigurationWithoutConditionalOnProperty.java b/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloTestConfigurationWithoutConditionalOnProperty.java deleted file mode 100644 index a723c1bb094892ff85cd91d721991df706bcb0cb..0000000000000000000000000000000000000000 --- a/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/ApolloTestConfigurationWithoutConditionalOnProperty.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.ctrip.framework.apollo.boot; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import com.ctrip.framework.apollo.boot.bean.TestBean; - -@Configuration -public class ApolloTestConfigurationWithoutConditionalOnProperty { - - @Bean - public TestBean testBean() { - return new TestBean(); - } -} diff --git a/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/bean/TestBean.java b/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/bean/TestBean.java deleted file mode 100644 index 42cb4d1e89739e57a2f12e461d467199f23e3d51..0000000000000000000000000000000000000000 --- a/apollo-boot-starter/src/test/java/com/ctrip/framework/apollo/boot/bean/TestBean.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.ctrip.framework.apollo.boot.bean; - -public class TestBean { - public boolean execute() { - return true; - } -} diff --git a/apollo-client/pom.xml b/apollo-client/pom.xml index 762755c7fbd302986c119438de18a4618984aa4d..cae8e1949550309fd8716904dec919cee1cd25dd 100644 --- a/apollo-client/pom.xml +++ b/apollo-client/pom.xml @@ -38,6 +38,12 @@ spring-context true + + + org.springframework.boot + spring-boot-autoconfigure + true + org.eclipse.jetty diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/boot/ApolloAutoConfiguration.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/boot/ApolloAutoConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..b519c25819484424352c53e0c5cc24cc08ff0a78 --- /dev/null +++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/boot/ApolloAutoConfiguration.java @@ -0,0 +1,20 @@ +package com.ctrip.framework.apollo.spring.boot; + +import com.ctrip.framework.apollo.spring.config.ConfigPropertySourcesProcessor; +import com.ctrip.framework.apollo.spring.config.PropertySourcesConstants; +import com.ctrip.framework.apollo.spring.config.PropertySourcesProcessor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConditionalOnProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED) +@ConditionalOnMissingBean(PropertySourcesProcessor.class) +public class ApolloAutoConfiguration { + + @Bean + public ConfigPropertySourcesProcessor configPropertySourcesProcessor() { + return new ConfigPropertySourcesProcessor(); + } +} diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/boot/ApolloSpringApplicationRunListener.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/boot/ApolloSpringApplicationRunListener.java new file mode 100644 index 0000000000000000000000000000000000000000..1a508ced0486c5add5b3e5c2491d3eee3079c4a5 --- /dev/null +++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/boot/ApolloSpringApplicationRunListener.java @@ -0,0 +1,93 @@ +package com.ctrip.framework.apollo.spring.boot; + +import com.ctrip.framework.apollo.Config; +import com.ctrip.framework.apollo.ConfigService; +import com.ctrip.framework.apollo.core.ConfigConsts; +import com.ctrip.framework.apollo.spring.config.PropertySourcesConstants; +import com.ctrip.framework.apollo.spring.config.ConfigPropertySource; +import com.google.common.base.Splitter; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.SpringApplicationRunListener; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.Ordered; +import org.springframework.core.PriorityOrdered; +import org.springframework.core.env.CompositePropertySource; +import org.springframework.core.env.ConfigurableEnvironment; + +/** + * Inject the Apollo config in Spring Boot bootstrap phase + * + *

Configuration example:

+ *
+ *   # will inject 'application' namespace in bootstrap phase
+ *   apollo.bootstrap.enabled = true
+ * 
+ * + * or + * + *
+ *   apollo.bootstrap.enabled = true
+ *   # will inject 'application' and 'TEST1.apollo' namespaces in bootstrap phase
+ *   apollo.bootstrap.namespaces = application,FX.apollo
+ * 
+ */ +public class ApolloSpringApplicationRunListener implements SpringApplicationRunListener, + PriorityOrdered { + private static final Logger logger = LoggerFactory.getLogger(ApolloSpringApplicationRunListener.class); + private static final Splitter NAMESPACE_SPLITTER = Splitter.on(",").omitEmptyStrings().trimResults(); + + public ApolloSpringApplicationRunListener(SpringApplication application, String[] args) { + //ignore + } + + public void starting() { + } + + public void started() { + } + + public void environmentPrepared(ConfigurableEnvironment environment) { + } + + public void contextPrepared(ConfigurableApplicationContext context) { + ConfigurableEnvironment environment = context.getEnvironment(); + 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); + return; + } + logger.debug("Apollo bootstrap config is enabled for context {}", context, PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED); + + if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) { + //already initialized + return; + } + + String namespaces = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES, ConfigConsts.NAMESPACE_APPLICATION); + logger.debug("Apollo bootstrap namespaces: {}", namespaces); + List namespaceList = NAMESPACE_SPLITTER.splitToList(namespaces); + + CompositePropertySource composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME); + for (String namespace : namespaceList) { + Config config = ConfigService.getConfig(namespace); + + composite.addPropertySource(new ConfigPropertySource(namespace, config)); + } + + environment.getPropertySources().addFirst(composite); + } + + public void contextLoaded(ConfigurableApplicationContext context) { + } + + public void finished(ConfigurableApplicationContext configurableApplicationContext, + Throwable throwable) { + } + + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE; + } +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..37b75d2809815fc73d2fa8201d8920791a5b698d --- /dev/null +++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/config/PropertySourcesConstants.java @@ -0,0 +1,8 @@ +package com.ctrip.framework.apollo.spring.config; + +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_NAMESPACES = "apollo.bootstrap.namespaces"; +} diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/config/PropertySourcesProcessor.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/config/PropertySourcesProcessor.java index d544cff9de1aaf1b7392cf91d802dbece3870f65..1c50aa3916a18b10f6c30a2c5286966b95956938 100644 --- a/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/config/PropertySourcesProcessor.java +++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/config/PropertySourcesProcessor.java @@ -31,7 +31,6 @@ import java.util.Iterator; * @author Jason Song(song_s@ctrip.com) */ public class PropertySourcesProcessor implements BeanFactoryPostProcessor, EnvironmentAware, PriorityOrdered { - private static final String APOLLO_PROPERTY_SOURCE_NAME = "ApolloPropertySources"; private static final Multimap NAMESPACE_NAMES = HashMultimap.create(); private ConfigurableEnvironment environment; @@ -46,11 +45,11 @@ public class PropertySourcesProcessor implements BeanFactoryPostProcessor, Envir } protected void initializePropertySources() { - if (environment.getPropertySources().contains(APOLLO_PROPERTY_SOURCE_NAME)) { + if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_PROPERTY_SOURCE_NAME)) { //already initialized return; } - CompositePropertySource composite = new CompositePropertySource(APOLLO_PROPERTY_SOURCE_NAME); + CompositePropertySource composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_PROPERTY_SOURCE_NAME); //sort by order asc ImmutableSortedSet orders = ImmutableSortedSet.copyOf(NAMESPACE_NAMES.keySet()); @@ -64,7 +63,15 @@ public class PropertySourcesProcessor implements BeanFactoryPostProcessor, Envir composite.addPropertySource(new ConfigPropertySource(namespace, config)); } } - environment.getPropertySources().addFirst(composite); + + // add after the bootstrap property source or to the first + if (environment.getPropertySources() + .contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) { + environment.getPropertySources() + .addAfter(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME, composite); + } else { + environment.getPropertySources().addFirst(composite); + } } @Override diff --git a/apollo-client/src/main/resources/META-INF/spring.factories b/apollo-client/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000000000000000000000000000000000000..1ab435c7d254ebefb40dd0ae18aa3f6d2fe9c118 --- /dev/null +++ b/apollo-client/src/main/resources/META-INF/spring.factories @@ -0,0 +1,4 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.ctrip.framework.apollo.spring.boot.ApolloAutoConfiguration +org.springframework.boot.SpringApplicationRunListener=\ +com.ctrip.framework.apollo.spring.boot.ApolloSpringApplicationRunListener diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/AllTests.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/AllTests.java index 9d89d0888dc15f4d2c500229a4465dd0f5826940..23926cb907f39321d229b7b627baf81c2bbf0f67 100644 --- a/apollo-client/src/test/java/com/ctrip/framework/apollo/AllTests.java +++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/AllTests.java @@ -1,6 +1,7 @@ package com.ctrip.framework.apollo; +import com.ctrip.framework.apollo.spring.BootstrapConfigTest; import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @@ -35,7 +36,7 @@ import com.ctrip.framework.apollo.util.parser.DurationParserTest; ConfigIntegrationTest.class, ExceptionUtilTest.class, XmlConfigFileTest.class, PropertiesConfigFileTest.class, RemoteConfigLongPollServiceTest.class, DateParserTest.class, DurationParserTest.class, JsonConfigFileTest.class, XmlConfigPlaceholderTest.class, JavaConfigPlaceholderTest.class, XMLConfigAnnotationTest.class, - JavaConfigAnnotationTest.class, ConfigUtilTest.class + JavaConfigAnnotationTest.class, ConfigUtilTest.class, BootstrapConfigTest.class }) public class AllTests { diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/AbstractSpringIntegrationTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/AbstractSpringIntegrationTest.java index f53077ef989925a8351eaefe67cb51e1ec050ec0..5bbd899307e90a9de7b6a2773c8bb3cb77d461d2 100644 --- a/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/AbstractSpringIntegrationTest.java +++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/AbstractSpringIntegrationTest.java @@ -37,6 +37,19 @@ public abstract class AbstractSpringIntegrationTest { @Before public void setUp() throws Exception { + doSetUp(); + } + + @After + public void tearDown() throws Exception { + doTearDown(); + } + + protected static void mockConfig(String namespace, Config config) { + CONFIG_REGISTRY.put(namespace, config); + } + + protected static void doSetUp() { //as PropertySourcesProcessor has some static states, so we must manually clear its state ReflectionUtils.invokeMethod(PROPERTY_SOURCES_PROCESSOR_CLEAR, null); //as ConfigService is singleton, so we must manually clear its container @@ -45,15 +58,10 @@ public abstract class AbstractSpringIntegrationTest { MockInjector.setInstance(ConfigManager.class, new MockConfigManager()); } - @After - public void tearDown() throws Exception { + protected static void doTearDown() { CONFIG_REGISTRY.clear(); } - protected void mockConfig(String namespace, Config config) { - CONFIG_REGISTRY.put(namespace, config); - } - public static class MockConfigManager implements ConfigManager { @Override 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 new file mode 100644 index 0000000000000000000000000000000000000000..747158f5cc8cf6452ad510155e5145bfab4a060a --- /dev/null +++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/spring/BootstrapConfigTest.java @@ -0,0 +1,295 @@ +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.config.PropertySourcesConstants; +import com.google.common.collect.Sets; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Jason Song(song_s@ctrip.com) + */ +@RunWith(Enclosed.class) +public class BootstrapConfigTest { + + private static final String TEST_BEAN_CONDITIONAL_ON_KEY = "apollo.test.testBean"; + private static final String FX_APOLLO_NAMESPACE = "FX.apollo"; + + @RunWith(SpringJUnit4ClassRunner.class) + @SpringApplicationConfiguration(ConfigurationWithConditionalOnProperty.class) + @DirtiesContext + public static class TestWithBootstrapEnabledAndDefaultNamespacesAndConditionalOn extends + AbstractSpringIntegrationTest { + private static final String someProperty = "someProperty"; + private static final String someValue = "someValue"; + + @Autowired(required = false) + private TestBean testBean; + + @ApolloConfig + private Config config; + + @Value("${" + someProperty + "}") + private String someInjectedValue; + + private static Config mockedConfig; + + + @BeforeClass + public static void beforeClass() throws Exception { + doSetUp(); + + System.setProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, "true"); + + mockedConfig = mock(Config.class); + + when(mockedConfig.getPropertyNames()).thenReturn(Sets.newHashSet(TEST_BEAN_CONDITIONAL_ON_KEY, someProperty)); + + when(mockedConfig.getProperty(eq(TEST_BEAN_CONDITIONAL_ON_KEY), anyString())).thenReturn(Boolean.TRUE.toString()); + when(mockedConfig.getProperty(eq(someProperty), anyString())).thenReturn(someValue); + + mockConfig(ConfigConsts.NAMESPACE_APPLICATION, mockedConfig); + } + + @AfterClass + public static void afterClass() throws Exception { + System.clearProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED); + + doTearDown(); + } + + @Test + public void test() throws Exception { + Assert.assertNotNull(testBean); + Assert.assertTrue(testBean.execute()); + + Assert.assertEquals(mockedConfig, config); + + Assert.assertEquals(someValue, someInjectedValue); + } + } + + @RunWith(SpringJUnit4ClassRunner.class) + @SpringApplicationConfiguration(ConfigurationWithConditionalOnProperty.class) + @DirtiesContext + public static class TestWithBootstrapEnabledAndNamespacesAndConditionalOn extends + AbstractSpringIntegrationTest { + + @Autowired(required = false) + private TestBean testBean; + + @BeforeClass + public static void beforeClass() throws Exception { + doSetUp(); + + System.setProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, "true"); + System.setProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES, + String.format("%s, %s", ConfigConsts.NAMESPACE_APPLICATION, FX_APOLLO_NAMESPACE)); + + Config config = mock(Config.class); + Config anotherConfig = mock(Config.class); + + when(config.getPropertyNames()).thenReturn(Sets.newHashSet(TEST_BEAN_CONDITIONAL_ON_KEY)); + when(config.getProperty(eq(TEST_BEAN_CONDITIONAL_ON_KEY), anyString())).thenReturn(Boolean.TRUE.toString()); + + mockConfig(ConfigConsts.NAMESPACE_APPLICATION, anotherConfig); + mockConfig(FX_APOLLO_NAMESPACE, config); + } + + @AfterClass + public static void afterClass() throws Exception { + System.clearProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED); + System.clearProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES); + + doTearDown(); + } + + @Test + public void test() throws Exception { + Assert.assertNotNull(testBean); + Assert.assertTrue(testBean.execute()); + } + } + + @RunWith(SpringJUnit4ClassRunner.class) + @SpringApplicationConfiguration(ConfigurationWithConditionalOnProperty.class) + @DirtiesContext + public static class TestWithBootstrapEnabledAndDefaultNamespacesAndConditionalOnFailed extends + AbstractSpringIntegrationTest { + + @Autowired(required = false) + private TestBean testBean; + + @BeforeClass + public static void beforeClass() throws Exception { + doSetUp(); + + System.setProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, "true"); + + Config config = mock(Config.class); + + when(config.getPropertyNames()).thenReturn(Sets.newHashSet(TEST_BEAN_CONDITIONAL_ON_KEY)); + when(config.getProperty(eq(TEST_BEAN_CONDITIONAL_ON_KEY), anyString())).thenReturn(Boolean.FALSE.toString()); + + mockConfig(ConfigConsts.NAMESPACE_APPLICATION, config); + } + + @AfterClass + public static void afterClass() throws Exception { + System.clearProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED); + + doTearDown(); + } + + @Test + public void test() throws Exception { + Assert.assertNull(testBean); + } + } + + @RunWith(SpringJUnit4ClassRunner.class) + @SpringApplicationConfiguration(ConfigurationWithoutConditionalOnProperty.class) + @DirtiesContext + public static class TestWithBootstrapEnabledAndDefaultNamespacesAndConditionalOff extends + AbstractSpringIntegrationTest { + + @Autowired(required = false) + private TestBean testBean; + + @BeforeClass + public static void beforeClass() throws Exception { + doSetUp(); + + System.setProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, "true"); + + Config config = mock(Config.class); + + mockConfig(ConfigConsts.NAMESPACE_APPLICATION, config); + } + + @AfterClass + public static void afterClass() throws Exception { + System.clearProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED); + + doTearDown(); + } + + @Test + public void test() throws Exception { + Assert.assertNotNull(testBean); + Assert.assertTrue(testBean.execute()); + } + } + + @RunWith(SpringJUnit4ClassRunner.class) + @SpringApplicationConfiguration(ConfigurationWithConditionalOnProperty.class) + @DirtiesContext + public static class TestWithBootstrapDisabledAndDefaultNamespacesAndConditionalOn extends + AbstractSpringIntegrationTest { + + @Autowired(required = false) + private TestBean testBean; + + @BeforeClass + public static void beforeClass() throws Exception { + doSetUp(); + + Config config = mock(Config.class); + + when(config.getPropertyNames()).thenReturn(Sets.newHashSet(TEST_BEAN_CONDITIONAL_ON_KEY)); + when(config.getProperty(eq(TEST_BEAN_CONDITIONAL_ON_KEY), anyString())).thenReturn(Boolean.FALSE.toString()); + + mockConfig(ConfigConsts.NAMESPACE_APPLICATION, config); + } + + @AfterClass + public static void afterClass() throws Exception { + doTearDown(); + } + + @Test + public void test() throws Exception { + Assert.assertNull(testBean); + } + } + + @RunWith(SpringJUnit4ClassRunner.class) + @SpringApplicationConfiguration(ConfigurationWithoutConditionalOnProperty.class) + @DirtiesContext + public static class TestWithBootstrapDisabledAndDefaultNamespacesAndConditionalOff extends + AbstractSpringIntegrationTest { + + @Autowired(required = false) + private TestBean testBean; + + @BeforeClass + public static void beforeClass() throws Exception { + doSetUp(); + + Config config = mock(Config.class); + + mockConfig(ConfigConsts.NAMESPACE_APPLICATION, config); + } + + @AfterClass + public static void afterClass() throws Exception { + System.clearProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED); + + doTearDown(); + } + + @Test + public void test() throws Exception { + Assert.assertNotNull(testBean); + Assert.assertTrue(testBean.execute()); + } + } + + @EnableAutoConfiguration + @Configuration + static class ConfigurationWithoutConditionalOnProperty { + + @Bean + public TestBean testBean() { + return new TestBean(); + } + } + + @ConditionalOnProperty(TEST_BEAN_CONDITIONAL_ON_KEY) + @EnableAutoConfiguration + @Configuration + static class ConfigurationWithConditionalOnProperty { + + @Bean + public TestBean testBean() { + return new TestBean(); + } + } + + static class TestBean { + + public boolean execute() { + return true; + } + } +} diff --git a/apollo-demo/src/main/java/com/ctrip/framework/apollo/demo/spring/springBootDemo/config/SampleRedisConfig.java b/apollo-demo/src/main/java/com/ctrip/framework/apollo/demo/spring/springBootDemo/config/SampleRedisConfig.java index 59ea1d4a0841646a2331a9e54522edbbc52e2950..43cbeb8a17cc5ce88bc6f07ab4125c97c608657b 100644 --- a/apollo-demo/src/main/java/com/ctrip/framework/apollo/demo/spring/springBootDemo/config/SampleRedisConfig.java +++ b/apollo-demo/src/main/java/com/ctrip/framework/apollo/demo/spring/springBootDemo/config/SampleRedisConfig.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.stereotype.Component; @@ -15,6 +16,7 @@ import javax.annotation.PostConstruct; /** * You may set up data like the following in Apollo: *
+ * redis.cache.enabled = true
  * redis.cache.expireSeconds = 100
  * redis.cache.clusterNodes = 1,2
  * redis.cache.commandTimeout = 50
@@ -24,8 +26,14 @@ import javax.annotation.PostConstruct;
  * redis.cache.someList[1] = d
  * 
* + * To make @ConditionalOnProperty work properly, apollo.bootstrap.enabled should be set to true + * and redis.cache.enabled should also be set to true. + * + * @see resources/bootstrap.yml + * * @author Jason Song(song_s@ctrip.com) */ +@ConditionalOnProperty("redis.cache.enabled") @ConfigurationProperties(prefix = "redis.cache") @Component("sampleRedisConfig") @RefreshScope diff --git a/apollo-demo/src/main/java/com/ctrip/framework/apollo/demo/spring/springBootDemo/refresh/SpringBootApolloRefreshConfig.java b/apollo-demo/src/main/java/com/ctrip/framework/apollo/demo/spring/springBootDemo/refresh/SpringBootApolloRefreshConfig.java index 1f97af4d8e95a00646a0b99d7f5e2e44a61fff9d..4f006717500e62d9a5f9c2f265bed55ba6559a8a 100644 --- a/apollo-demo/src/main/java/com/ctrip/framework/apollo/demo/spring/springBootDemo/refresh/SpringBootApolloRefreshConfig.java +++ b/apollo-demo/src/main/java/com/ctrip/framework/apollo/demo/spring/springBootDemo/refresh/SpringBootApolloRefreshConfig.java @@ -8,12 +8,14 @@ import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.context.scope.refresh.RefreshScope; import org.springframework.stereotype.Component; /** * @author Jason Song(song_s@ctrip.com) */ +@ConditionalOnProperty("redis.cache.enabled") @Component public class SpringBootApolloRefreshConfig { private static final Logger logger = LoggerFactory.getLogger(SpringBootApolloRefreshConfig.class); diff --git a/apollo-demo/src/main/resources/application.yml b/apollo-demo/src/main/resources/application.yml new file mode 100644 index 0000000000000000000000000000000000000000..80857e796a1a3120e7fc5c78b9d50cb6733174d9 --- /dev/null +++ b/apollo-demo/src/main/resources/application.yml @@ -0,0 +1,5 @@ +apollo: + bootstrap: + enabled: true + # will inject 'application' and 'FX.apollo' namespaces in bootstrap phase + namespaces: application,FX.apollo diff --git a/pom.xml b/pom.xml index a897b40396c206ddda266a2b49200ab967f68381..a86ce80eb549fb28c8d0a0ee86f974517717104c 100644 --- a/pom.xml +++ b/pom.xml @@ -101,7 +101,6 @@ apollo-portal apollo-assembly apollo-demo - apollo-boot-starter