Defensively expect concurrent registration of BeanPostProcessors

Declaring beanPostProcessors (and also embeddedValueResolvers) as CopyOnWriteArrayList prevents ConcurrentModificationExceptions in case of concurrent registration/access attempts.

Issue: SPR-17286
This commit is contained in:
Juergen Hoeller
2018-09-18 22:09:01 +02:00
parent ec1aa5c6ea
commit 0d1bf52122

View File

@@ -30,11 +30,11 @@ import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapper;
@@ -142,16 +142,16 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
private TypeConverter typeConverter; private TypeConverter typeConverter;
/** String resolvers to apply e.g. to annotation attribute values */ /** String resolvers to apply e.g. to annotation attribute values */
private final List<StringValueResolver> embeddedValueResolvers = new LinkedList<StringValueResolver>(); private final List<StringValueResolver> embeddedValueResolvers = new CopyOnWriteArrayList<StringValueResolver>();
/** BeanPostProcessors to apply in createBean */ /** BeanPostProcessors to apply in createBean */
private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<BeanPostProcessor>(); private final List<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<BeanPostProcessor>();
/** Indicates whether any InstantiationAwareBeanPostProcessors have been registered */ /** Indicates whether any InstantiationAwareBeanPostProcessors have been registered */
private boolean hasInstantiationAwareBeanPostProcessors; private volatile boolean hasInstantiationAwareBeanPostProcessors;
/** Indicates whether any DestructionAwareBeanPostProcessors have been registered */ /** Indicates whether any DestructionAwareBeanPostProcessors have been registered */
private boolean hasDestructionAwareBeanPostProcessors; private volatile boolean hasDestructionAwareBeanPostProcessors;
/** Map from scope identifier String to corresponding Scope */ /** Map from scope identifier String to corresponding Scope */
private final Map<String, Scope> scopes = new LinkedHashMap<String, Scope>(8); private final Map<String, Scope> scopes = new LinkedHashMap<String, Scope>(8);
@@ -845,14 +845,17 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
@Override @Override
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) { public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null"); Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
// Remove from old position, if any
this.beanPostProcessors.remove(beanPostProcessor); this.beanPostProcessors.remove(beanPostProcessor);
this.beanPostProcessors.add(beanPostProcessor); // Track whether it is instantiation/destruction aware
if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) { if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
this.hasInstantiationAwareBeanPostProcessors = true; this.hasInstantiationAwareBeanPostProcessors = true;
} }
if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) { if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
this.hasDestructionAwareBeanPostProcessors = true; this.hasDestructionAwareBeanPostProcessors = true;
} }
// Add to end of list
this.beanPostProcessors.add(beanPostProcessor);
} }
@Override @Override
@@ -982,7 +985,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
@Override @Override
public BeanDefinition getMergedBeanDefinition(String name) throws BeansException { public BeanDefinition getMergedBeanDefinition(String name) throws BeansException {
String beanName = transformedBeanName(name); String beanName = transformedBeanName(name);
// Efficiently check whether bean definition exists in this factory. // Efficiently check whether bean definition exists in this factory.
if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) { if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) {
return ((ConfigurableBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(beanName); return ((ConfigurableBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(beanName);
@@ -994,7 +996,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
@Override @Override
public boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException { public boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException {
String beanName = transformedBeanName(name); String beanName = transformedBeanName(name);
Object beanInstance = getSingleton(beanName, false); Object beanInstance = getSingleton(beanName, false);
if (beanInstance != null) { if (beanInstance != null) {
return (beanInstance instanceof FactoryBean); return (beanInstance instanceof FactoryBean);
@@ -1003,13 +1004,11 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
// null instance registered // null instance registered
return false; return false;
} }
// No singleton instance found -> check bean definition. // No singleton instance found -> check bean definition.
if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) { if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) {
// No bean definition found in this factory -> delegate to parent. // No bean definition found in this factory -> delegate to parent.
return ((ConfigurableBeanFactory) getParentBeanFactory()).isFactoryBean(name); return ((ConfigurableBeanFactory) getParentBeanFactory()).isFactoryBean(name);
} }
return isFactoryBean(beanName, getMergedLocalBeanDefinition(beanName)); return isFactoryBean(beanName, getMergedLocalBeanDefinition(beanName));
} }