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:
@@ -29,11 +29,11 @@ import java.util.HashMap;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
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;
|
||||||
@@ -145,16 +145,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<>();
|
private final List<StringValueResolver> embeddedValueResolvers = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
/** BeanPostProcessors to apply in createBean */
|
/** BeanPostProcessors to apply in createBean */
|
||||||
private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();
|
private final List<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
/** 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<>(8);
|
private final Map<String, Scope> scopes = new LinkedHashMap<>(8);
|
||||||
@@ -847,14 +847,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
|
||||||
@@ -985,7 +988,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);
|
||||||
@@ -997,18 +999,15 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user