Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support apollo config injection in Spring Boot bootstrap phase #937

Merged
merged 3 commits into from
Jan 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这句日志少了一个大括号{}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

。。。这个日志不需要传PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED这个参数。。。可以提个PR改一下。。。或者晚点我改一下也行


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