com.ctrip.framework.apollo.spring.config.PropertySourcesProcessor Maven / Gradle / Ivy
The newest version!
package com.ctrip.framework.apollo.spring.config;
import com.ctrip.framework.apollo.build.ApolloInjector;
import com.ctrip.framework.apollo.spring.property.AutoUpdateConfigChangeListener;
import com.ctrip.framework.apollo.spring.util.SpringInjector;
import com.ctrip.framework.apollo.util.ConfigUtil;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigService;
import com.google.common.collect.Sets;
import java.util.List;
import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import java.util.Collection;
import java.util.Iterator;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
/**
* Apollo Property Sources processor for Spring Annotation Based Application.
*
* The reason why PropertySourcesProcessor implements {@link BeanFactoryPostProcessor} instead of
* {@link org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor} is that lower versions of
* Spring (e.g. 3.1.1) doesn't support registering BeanDefinitionRegistryPostProcessor in ImportBeanDefinitionRegistrar
* - {@link com.ctrip.framework.apollo.spring.annotation.ApolloConfigRegistrar}
*
* @author Jason Song([email protected])
*/
public class PropertySourcesProcessor implements BeanFactoryPostProcessor, EnvironmentAware, PriorityOrdered {
private static final Multimap NAMESPACE_NAMES = LinkedHashMultimap.create();
private static final Set AUTO_UPDATE_INITIALIZED_BEAN_FACTORIES = Sets.newConcurrentHashSet();
private final ConfigPropertySourceFactory configPropertySourceFactory = SpringInjector
.getInstance(ConfigPropertySourceFactory.class);
private final ConfigUtil configUtil = ApolloInjector.getInstance(ConfigUtil.class);
private ConfigurableEnvironment environment;
public static boolean addNamespaces(Collection namespaces, int order) {
return NAMESPACE_NAMES.putAll(order, namespaces);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
initializePropertySources();
initializeAutoUpdatePropertiesFeature(beanFactory);
}
private void initializePropertySources() {
if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_PROPERTY_SOURCE_NAME)) {
//already initialized
return;
}
CompositePropertySource composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_PROPERTY_SOURCE_NAME);
//sort by order asc
ImmutableSortedSet orders = ImmutableSortedSet.copyOf(NAMESPACE_NAMES.keySet());
Iterator iterator = orders.iterator();
while (iterator.hasNext()) {
int order = iterator.next();
for (String namespace : NAMESPACE_NAMES.get(order)) {
Config config = ConfigService.getConfig(namespace);
composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
}
}
// clean up
NAMESPACE_NAMES.clear();
// add after the bootstrap property source or to the first
if (environment.getPropertySources()
.contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
// ensure ApolloBootstrapPropertySources is still the first
ensureBootstrapPropertyPrecedence(environment);
environment.getPropertySources()
.addAfter(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME, composite);
} else {
environment.getPropertySources().addFirst(composite);
}
}
private void ensureBootstrapPropertyPrecedence(ConfigurableEnvironment environment) {
MutablePropertySources propertySources = environment.getPropertySources();
PropertySource> bootstrapPropertySource = propertySources
.get(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME);
// not exists or already in the first place
if (bootstrapPropertySource == null || propertySources.precedenceOf(bootstrapPropertySource) == 0) {
return;
}
propertySources.remove(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME);
propertySources.addFirst(bootstrapPropertySource);
}
private void initializeAutoUpdatePropertiesFeature(ConfigurableListableBeanFactory beanFactory) {
if (!configUtil.isAutoUpdateInjectedSpringPropertiesEnabled() ||
!AUTO_UPDATE_INITIALIZED_BEAN_FACTORIES.add(beanFactory)) {
return;
}
AutoUpdateConfigChangeListener autoUpdateConfigChangeListener = new AutoUpdateConfigChangeListener(
environment, beanFactory);
List configPropertySources = configPropertySourceFactory.getAllConfigPropertySources();
for (ConfigPropertySource configPropertySource : configPropertySources) {
configPropertySource.addChangeListener(autoUpdateConfigChangeListener);
}
}
@Override
public void setEnvironment(Environment environment) {
//it is safe enough to cast as all known environment is derived from ConfigurableEnvironment
this.environment = (ConfigurableEnvironment) environment;
}
@Override
public int getOrder() {
//make it as early as possible
return Ordered.HIGHEST_PRECEDENCE;
}
// for test only
static void reset() {
NAMESPACE_NAMES.clear();
AUTO_UPDATE_INITIALIZED_BEAN_FACTORIES.clear();
}
}