Skip to content

ConcurrentModificationException thrown while iterating over bean definition names in DefaultListableBeanFactory#getBeansWithAnnotation(Class<? extends Annotation> annotationType) [SPR-12688] #17286

Closed
@spring-projects-issues

Description

@spring-projects-issues

Chip Killmar opened SPR-12688 and commented

During the creation of one of our beans, we call getBeansWithAnnotation(Class<? extends Annotation> annotationType) to get a map of lazy-loaded annotated beans. This method instantiates these annotated beans using getBean(String beanName).

In our configuration, we're dynamically registering Spring beans using a post processor bean. The post processor bean receives a callback whenever one of these annotated beans is created, and dynamically registers new bean definitions.

The problem is that dynamic registration modifies List<String> beanDefinitionNames while Spring is iterating over it, causing a java.util.ConcurrentModificationException to be thrown:

Caused by: java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
	at java.util.ArrayList$Itr.next(ArrayList.java:851)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansWithAnnotation(DefaultListableBeanFactory.java:566)
	at com.homeaway.TestConfiguration.testBean(TestConfiguration.java:43)
	at com.homeaway.TestConfiguration$$EnhancerBySpringCGLIB$$c0d0df0c.CGLIB$testBean$0(<generated>)
	at com.homeaway.TestConfiguration$$EnhancerBySpringCGLIB$$c0d0df0c$$FastClassBySpringCGLIB$$7a010a97.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
	at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:309)
	at com.homeaway.TestConfiguration$$EnhancerBySpringCGLIB$$c0d0df0c.testBean(<generated>)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162)
	... 36 more

Spring 3.2.2.RELEASE doesn't exhibit the same behavior because it creates a local set of bean names and iterates over that:

   // 3.2.2.RELEASE, DefaultListableBeanFactory.java
   public Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) {
     Set<String> beanNames = new LinkedHashSet<String>(getBeanDefinitionCount());
     beanNames.addAll(Arrays.asList(getBeanDefinitionNames()));
     beanNames.addAll(Arrays.asList(getSingletonNames()));
     Map<String, Object> results = new LinkedHashMap<String, Object>();
     for (String beanName : beanNames) {
          if (findAnnotationOnBean(beanName, annotationType) != null) {
               results.put(beanName, getBean(beanName));
          }
     }
     return results;
}
   // 4.1.4.RELEASE, DefaultListableBeanFactory.java
@Override
public Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) {
     Map<String, Object> results = new LinkedHashMap<String, Object>();
     for (String beanName : this.beanDefinitionNames) {
          BeanDefinition beanDefinition = getBeanDefinition(beanName);
          if (!beanDefinition.isAbstract() && findAnnotationOnBean(beanName, annotationType) != null) {
               results.put(beanName, getBean(beanName));
          }
     }
     for (String beanName : this.manualSingletonNames) {
          if (!results.containsKey(beanName) && findAnnotationOnBean(beanName, annotationType) != null) {
               results.put(beanName, getBean(beanName));
          }
     }
     return results;
}

As a workaround for Spring 4.1.4.RELEASE, I've implemented a separate version of getBeansWithAnnotation(Class<? extends Annotation> annotationType as a custom method outside of Spring using the logic in 3.2.2.RELEASE.

I've attached an isolated test configuration and unit test that demonstrates the problem.


Affects: 4.1.4

Attachments:

Issue Links:

Referenced from: commits 918bc3b, 6c47b5f

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)type: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions