Ensure bean definitions can be removed concurrently
Prior to this commit, concurrent invocations of DefaultListableBeanFactory.removeBeanDefinition() could result in a NullPointerException. This commit fixes this by adding an appropriate not-null check in resetBeanDefinition(). Closes gh-23542
This commit is contained in:
parent
ca2b2f5533
commit
b2aad1c3b1
|
@ -1003,7 +1003,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||||
for (String bdName : this.beanDefinitionNames) {
|
for (String bdName : this.beanDefinitionNames) {
|
||||||
if (!beanName.equals(bdName)) {
|
if (!beanName.equals(bdName)) {
|
||||||
BeanDefinition bd = this.beanDefinitionMap.get(bdName);
|
BeanDefinition bd = this.beanDefinitionMap.get(bdName);
|
||||||
if (beanName.equals(bd.getParentName())) {
|
// Ensure bd is non-null due to potential concurrent modification
|
||||||
|
// of the beanDefinitionMap.
|
||||||
|
if (bd != null && beanName.equals(bd.getParentName())) {
|
||||||
resetBeanDefinition(bdName);
|
resetBeanDefinition(bdName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
import javax.annotation.Priority;
|
import javax.annotation.Priority;
|
||||||
import javax.security.auth.Subject;
|
import javax.security.auth.Subject;
|
||||||
|
@ -817,6 +818,32 @@ public class DefaultListableBeanFactoryTests {
|
||||||
assertTrue(lbf.getBean("test2") instanceof NestedTestBean);
|
assertTrue(lbf.getBean("test2") instanceof NestedTestBean);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // gh-23542
|
||||||
|
public void concurrentBeanDefinitionRemoval() {
|
||||||
|
final int MAX = 200;
|
||||||
|
lbf.setAllowBeanDefinitionOverriding(false);
|
||||||
|
|
||||||
|
// Register the bean definitions before invoking preInstantiateSingletons()
|
||||||
|
// to simulate realistic usage of an ApplicationContext; otherwise, the bean
|
||||||
|
// factory thinks it's an "empty" factory which causes this test to fail in
|
||||||
|
// an unrealistic manner.
|
||||||
|
IntStream.range(0, MAX).forEach(this::registerTestBean);
|
||||||
|
lbf.preInstantiateSingletons();
|
||||||
|
|
||||||
|
// This test is considered successful if the following does not result in an exception.
|
||||||
|
IntStream.range(0, MAX).parallel().forEach(this::removeTestBean);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerTestBean(int i) {
|
||||||
|
String name = "test" + i;
|
||||||
|
lbf.registerBeanDefinition(name, new RootBeanDefinition(TestBean.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeTestBean(int i) {
|
||||||
|
String name = "test" + i;
|
||||||
|
lbf.removeBeanDefinition(name);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBeanDefinitionOverridingNotAllowed() {
|
public void testBeanDefinitionOverridingNotAllowed() {
|
||||||
lbf.setAllowBeanDefinitionOverriding(false);
|
lbf.setAllowBeanDefinitionOverriding(false);
|
||||||
|
|
Loading…
Reference in New Issue