Skip to content

Commit

Permalink
support multiple spring contexts
Browse files Browse the repository at this point in the history
  • Loading branch information
nobodyiam committed Sep 3, 2018
1 parent 2a926ea commit ccc3afb
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 64 deletions.
Expand Up @@ -65,7 +65,7 @@ protected void processField(Object bean, String beanName, Field field) {
Set<String> keys = placeholderHelper.extractPlaceholderKeys(placeholder);
for (String key : keys) {
SpringValue springValue = new SpringValue(key, placeholder, bean, beanName, field, true);
springValueRegistry.register(key, springValue);
springValueRegistry.register(beanFactory, key, springValue);
logger.debug("Monitoring {}", springValue);
}
}
Expand Down Expand Up @@ -102,7 +102,7 @@ protected void processMethod(Object bean, String beanName, Method method) {
for (String key : keys) {
SpringValue springValue = new SpringValue(key, apolloJsonValue.value(), bean, beanName,
method, true);
springValueRegistry.register(key, springValue);
springValueRegistry.register(beanFactory, key, springValue);
logger.debug("Monitoring {}", springValue);
}
}
Expand Down
Expand Up @@ -19,9 +19,12 @@
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Bean;

/**
Expand All @@ -30,29 +33,30 @@
* @author github.com/zhegexiaohuozi seimimaster@gmail.com
* @since 2017/12/20.
*/
public class SpringValueProcessor extends ApolloProcessor implements BeanFactoryPostProcessor {
public class SpringValueProcessor extends ApolloProcessor implements BeanFactoryPostProcessor, BeanFactoryAware {

private static final Logger logger = LoggerFactory.getLogger(SpringValueProcessor.class);

private final ConfigUtil configUtil;
private final PlaceholderHelper placeholderHelper;
private final SpringValueRegistry springValueRegistry;

private static Multimap<String, SpringValueDefinition> beanName2SpringValueDefinitions =
LinkedListMultimap.create();
private BeanFactory beanFactory;
private Multimap<String, SpringValueDefinition> beanName2SpringValueDefinitions;

public SpringValueProcessor() {
configUtil = ApolloInjector.getInstance(ConfigUtil.class);
placeholderHelper = SpringInjector.getInstance(PlaceholderHelper.class);
springValueRegistry = SpringInjector.getInstance(SpringValueRegistry.class);
beanName2SpringValueDefinitions = LinkedListMultimap.create();
}

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
if (configUtil.isAutoUpdateInjectedSpringPropertiesEnabled()) {
if (configUtil.isAutoUpdateInjectedSpringPropertiesEnabled() && beanFactory instanceof BeanDefinitionRegistry) {
beanName2SpringValueDefinitions = SpringValueDefinitionProcessor
.getBeanName2SpringValueDefinitions();
.getBeanName2SpringValueDefinitions((BeanDefinitionRegistry) beanFactory);
}
}

Expand Down Expand Up @@ -82,7 +86,7 @@ protected void processField(Object bean, String beanName, Field field) {

for (String key : keys) {
SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, field, false);
springValueRegistry.register(key, springValue);
springValueRegistry.register(beanFactory, key, springValue);
logger.debug("Monitoring {}", springValue);
}
}
Expand Down Expand Up @@ -112,7 +116,7 @@ protected void processMethod(Object bean, String beanName, Method method) {

for (String key : keys) {
SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, method, false);
springValueRegistry.register(key, springValue);
springValueRegistry.register(beanFactory, key, springValue);
logger.info("Monitoring {}", springValue);
}
}
Expand All @@ -135,7 +139,7 @@ private void processBeanPropertyValues(Object bean, String beanName) {
}
SpringValue springValue = new SpringValue(definition.getKey(), definition.getPlaceholder(),
bean, beanName, method, false);
springValueRegistry.register(definition.getKey(), springValue);
springValueRegistry.register(beanFactory, definition.getKey(), springValue);
logger.debug("Monitoring {}", springValue);
} catch (Throwable ex) {
logger.error("Failed to enable auto update feature for {}.{}", bean.getClass(),
Expand All @@ -147,4 +151,8 @@ private void processBeanPropertyValues(Object bean, String beanName) {
beanName2SpringValueDefinitions.removeAll(beanName);
}

@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
Expand Up @@ -11,9 +11,11 @@
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.concurrent.atomic.AtomicBoolean;
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;
Expand All @@ -40,7 +42,7 @@
*/
public class PropertySourcesProcessor implements BeanFactoryPostProcessor, EnvironmentAware, PriorityOrdered {
private static final Multimap<Integer, String> NAMESPACE_NAMES = LinkedHashMultimap.create();
private static final AtomicBoolean INITIALIZED = new AtomicBoolean(false);
private static final Set<BeanFactory> AUTO_UPDATE_INITIALIZED_BEAN_FACTORIES = Sets.newConcurrentHashSet();

private final ConfigPropertySourceFactory configPropertySourceFactory = SpringInjector
.getInstance(ConfigPropertySourceFactory.class);
Expand All @@ -53,11 +55,8 @@ public static boolean addNamespaces(Collection<String> namespaces, int order) {

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (INITIALIZED.compareAndSet(false, true)) {
initializePropertySources();

initializeAutoUpdatePropertiesFeature(beanFactory);
}
initializePropertySources();
initializeAutoUpdatePropertiesFeature(beanFactory);
}

private void initializePropertySources() {
Expand All @@ -80,6 +79,9 @@ private void initializePropertySources() {
}
}

// 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)) {
Expand Down Expand Up @@ -110,7 +112,8 @@ private void ensureBootstrapPropertyPrecedence(ConfigurableEnvironment environme
}

private void initializeAutoUpdatePropertiesFeature(ConfigurableListableBeanFactory beanFactory) {
if (!configUtil.isAutoUpdateInjectedSpringPropertiesEnabled()) {
if (!configUtil.isAutoUpdateInjectedSpringPropertiesEnabled() ||
!AUTO_UPDATE_INITIALIZED_BEAN_FACTORIES.add(beanFactory)) {
return;
}

Expand All @@ -129,12 +132,6 @@ public void setEnvironment(Environment environment) {
this.environment = (ConfigurableEnvironment) environment;
}

//only for test
private static void reset() {
NAMESPACE_NAMES.clear();
INITIALIZED.set(false);
}

@Override
public int getOrder() {
//make it as early as possible
Expand Down
Expand Up @@ -51,7 +51,7 @@ public void onChange(ConfigChangeEvent changeEvent) {
}
for (String key : keys) {
// 1. check whether the changed key is relevant
Collection<SpringValue> targetValues = springValueRegistry.get(key);
Collection<SpringValue> targetValues = springValueRegistry.get(beanFactory, key);
if (targetValues == null || targetValues.isEmpty()) {
continue;
}
Expand Down
@@ -1,10 +1,12 @@
package com.ctrip.framework.apollo.spring.property;

import com.ctrip.framework.apollo.spring.util.SpringInjector;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.util.List;
import java.util.Map;
import java.util.Set;

import java.util.concurrent.atomic.AtomicBoolean;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
Expand All @@ -30,9 +32,9 @@
* </pre>
*/
public class SpringValueDefinitionProcessor implements BeanDefinitionRegistryPostProcessor {
private static final Multimap<String, SpringValueDefinition> beanName2SpringValueDefinitions =
LinkedListMultimap.create();
private static final AtomicBoolean initialized = new AtomicBoolean(false);
private static final Map<BeanDefinitionRegistry, Multimap<String, SpringValueDefinition>> beanName2SpringValueDefinitions =
Maps.newConcurrentMap();
private static final Set<BeanDefinitionRegistry> PROPERTY_VALUES_PROCESSED_BEAN_FACTORIES = Sets.newConcurrentHashSet();

private final ConfigUtil configUtil;
private final PlaceholderHelper placeholderHelper;
Expand All @@ -54,16 +56,27 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)

}

public static Multimap<String, SpringValueDefinition> getBeanName2SpringValueDefinitions() {
return beanName2SpringValueDefinitions;
public static Multimap<String, SpringValueDefinition> getBeanName2SpringValueDefinitions(BeanDefinitionRegistry registry) {
Multimap<String, SpringValueDefinition> springValueDefinitions = beanName2SpringValueDefinitions.get(registry);
if (springValueDefinitions == null) {
springValueDefinitions = LinkedListMultimap.create();
}

return springValueDefinitions;
}

private void processPropertyValues(BeanDefinitionRegistry beanRegistry) {
if (!initialized.compareAndSet(false, true)) {
if (!PROPERTY_VALUES_PROCESSED_BEAN_FACTORIES.add(beanRegistry)) {
// already initialized
return;
}

if (!beanName2SpringValueDefinitions.containsKey(beanRegistry)) {
beanName2SpringValueDefinitions.put(beanRegistry, LinkedListMultimap.<String, SpringValueDefinition>create());
}

Multimap<String, SpringValueDefinition> springValueDefinitions = beanName2SpringValueDefinitions.get(beanRegistry);

String[] beanNames = beanRegistry.getBeanDefinitionNames();
for (String beanName : beanNames) {
BeanDefinition beanDefinition = beanRegistry.getBeanDefinition(beanName);
Expand All @@ -82,16 +95,9 @@ private void processPropertyValues(BeanDefinitionRegistry beanRegistry) {
}

for (String key : keys) {
beanName2SpringValueDefinitions.put(beanName,
new SpringValueDefinition(key, placeholder, propertyValue.getName()));
springValueDefinitions.put(beanName, new SpringValueDefinition(key, placeholder, propertyValue.getName()));
}
}
}
}

//only for test
private static void reset() {
initialized.set(false);
beanName2SpringValueDefinitions.clear();
}
}
@@ -1,17 +1,34 @@
package com.ctrip.framework.apollo.spring.property;

import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import java.util.Collection;
import java.util.Map;
import org.springframework.beans.factory.BeanFactory;

public class SpringValueRegistry {
private final Multimap<String, SpringValue> registry = LinkedListMultimap.create();

public void register(String key, SpringValue springValue) {
registry.put(key, springValue);
private final Map<BeanFactory, Multimap<String, SpringValue>> registry = Maps.newConcurrentMap();
private final Object LOCK = new Object();

public void register(BeanFactory beanFactory, String key, SpringValue springValue) {
if (!registry.containsKey(beanFactory)) {
synchronized (LOCK) {
if (!registry.containsKey(beanFactory)) {
registry.put(beanFactory, LinkedListMultimap.<String, SpringValue>create());
}
}
}

registry.get(beanFactory).put(key, springValue);
}

public Collection<SpringValue> get(String key) {
return registry.get(key);
public Collection<SpringValue> get(BeanFactory beanFactory, String key) {
Multimap<String, SpringValue> beanFactorySpringValues = registry.get(beanFactory);
if (beanFactorySpringValues == null) {
return null;
}
return beanFactorySpringValues.get(key);
}
}
Expand Up @@ -33,16 +33,10 @@
*/
public abstract class AbstractSpringIntegrationTest {
private static final Map<String, Config> CONFIG_REGISTRY = Maps.newHashMap();
private static Method PROPERTY_SOURCES_PROCESSOR_CLEAR;
private static Method SPRING_VALUE_DEFINITION_PROCESS_CLEAR;
private static Method CONFIG_SERVICE_RESET;

static {
try {
PROPERTY_SOURCES_PROCESSOR_CLEAR = PropertySourcesProcessor.class.getDeclaredMethod("reset");
ReflectionUtils.makeAccessible(PROPERTY_SOURCES_PROCESSOR_CLEAR);
SPRING_VALUE_DEFINITION_PROCESS_CLEAR = SpringValueDefinitionProcessor.class.getDeclaredMethod("reset");
ReflectionUtils.makeAccessible(SPRING_VALUE_DEFINITION_PROCESS_CLEAR);
CONFIG_SERVICE_RESET = ConfigService.class.getDeclaredMethod("reset");
ReflectionUtils.makeAccessible(CONFIG_SERVICE_RESET);
} catch (NoSuchMethodException e) {
Expand Down Expand Up @@ -112,10 +106,6 @@ protected static void mockConfig(String namespace, Config 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 SpringValueDefinitionProcessor has some static states, so we must manually clear its state
ReflectionUtils.invokeMethod(SPRING_VALUE_DEFINITION_PROCESS_CLEAR, null);
//as ConfigService is singleton, so we must manually clear its container
ReflectionUtils.invokeMethod(CONFIG_SERVICE_RESET, null);
MockInjector.reset();
Expand Down
Expand Up @@ -37,8 +37,6 @@ public class EmbeddedApollo extends ExternalResource {
private static final Type notificationType = new TypeToken<List<ApolloConfigNotification>>() {
}.getType();

private static Method PROPERTY_SOURCES_PROCESSOR_CLEAR;
private static Method SPRING_VALUE_DEFINITION_PROCESS_CLEAR;
private static Method CONFIG_SERVICE_LOCATOR_CLEAR;
private static ConfigServiceLocator CONFIG_SERVICE_LOCATOR;

Expand All @@ -51,10 +49,6 @@ public class EmbeddedApollo extends ExternalResource {
static {
try {
System.setProperty("apollo.longPollingInitialDelayInMills", "0");
PROPERTY_SOURCES_PROCESSOR_CLEAR = PropertySourcesProcessor.class.getDeclaredMethod("reset");
PROPERTY_SOURCES_PROCESSOR_CLEAR.setAccessible(true);
SPRING_VALUE_DEFINITION_PROCESS_CLEAR = SpringValueDefinitionProcessor.class.getDeclaredMethod("reset");
SPRING_VALUE_DEFINITION_PROCESS_CLEAR.setAccessible(true);
CONFIG_SERVICE_LOCATOR = ApolloInjector.getInstance(ConfigServiceLocator.class);
CONFIG_SERVICE_LOCATOR_CLEAR = ConfigServiceLocator.class.getDeclaredMethod("initConfigServices");
CONFIG_SERVICE_LOCATOR_CLEAR.setAccessible(true);
Expand Down Expand Up @@ -105,9 +99,6 @@ protected void after() {

private void clear() throws Exception {
resetOverriddenProperties();
// clear Apollo states
PROPERTY_SOURCES_PROCESSOR_CLEAR.invoke(null);
SPRING_VALUE_DEFINITION_PROCESS_CLEAR.invoke(null);
}

private void mockConfigServiceUrl(String url) throws Exception {
Expand Down

0 comments on commit ccc3afb

Please sign in to comment.