Skip to content

Commit

Permalink
Merge pull request #937 from nobodyiam/apollo.bootstrap
Browse files Browse the repository at this point in the history
Support apollo config injection in Spring Boot bootstrap phase
  • Loading branch information
nobodyiam committed Jan 28, 2018
2 parents 39dc297 + cbb0224 commit e7b9f0d
Show file tree
Hide file tree
Showing 12 changed files with 468 additions and 11 deletions.
6 changes: 6 additions & 0 deletions apollo-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@
<artifactId>spring-context</artifactId>
<optional>true</optional>
</dependency>
<!-- optional spring boot dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<optional>true</optional>
</dependency>
<!-- test -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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
*
* <p>Configuration example:</p>
* <pre class="code">
* # will inject 'application' namespace in bootstrap phase
* apollo.bootstrap.enabled = true
* </pre>
*
* or
*
* <pre class="code">
* apollo.bootstrap.enabled = true
* # will inject 'application' and 'FX.apollo' namespaces in bootstrap phase
* apollo.bootstrap.namespaces = application,FX.apollo
* </pre>
*/
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<String> 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;
}
}
Original file line number Diff line number Diff line change
@@ -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";
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
* @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<Integer, String> NAMESPACE_NAMES = HashMultimap.create();

private ConfigurableEnvironment environment;
Expand All @@ -46,11 +45,11 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
}

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<Integer> orders = ImmutableSortedSet.copyOf(NAMESPACE_NAMES.keySet());
Expand All @@ -64,7 +63,15 @@ protected void initializePropertySources() {
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
Expand Down
4 changes: 4 additions & 0 deletions apollo-client/src/main/resources/META-INF/spring.factories
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -35,7 +36,7 @@
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 {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -45,15 +58,10 @@ public void setUp() throws Exception {
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
Expand Down

0 comments on commit e7b9f0d

Please sign in to comment.