Announcement Announcement Module
Collapse
No announcement yet.
ConcurrentModificationException in InjectionMetadata class Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • ConcurrentModificationException in InjectionMetadata class

    Hi. I am getting a ConcurrentModificationException exception in the Spring's InjectionMetadata class (located in the org.springframework.beans.factory.annotation package) when creating an instance of prototype bean in a multithreaded environment.

    Here's the stack of the exception (only Spring portion of it):

    Code:
    Thread [MaintenanceTasksJob] (Suspended (exception ConcurrentModificationException))                  
    LinkedHashMap$KeyIterator(LinkedHashMap$LinkedHashIterator<T>).remove() line: 364
    InjectionMetadata.checkConfigMembers(RootBeanDefinition) line: 72
    AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition(RootBeanDefinition, Class, String) line: 216
    DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).applyMergedBeanDefinitionPostProcessors(RootBeanDefinition, Class, String) line: 789
    DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).doCreateBean(String, RootBeanDefinition, Object[]) line: 487
    DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBean(String, RootBeanDefinition, Object[]) line: 450
    DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class<T>, Object[], boolean) line: 309
    DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 189
    ClassPathXmlApplicationContext(AbstractApplicationContext).getBean(String) line: 1044
    I think I know what's going on here. The AbstractAutowireCapableBeanFactory.doCreateBean() method (5th from the top of the stack) synchronizes on the bean definition object:

    Code:
    synchronized (mbd.postProcessingLock) {
      if (!mbd.postProcessed) {
        applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
        mbd.postProcessed = true;
      }
    }
    However, two methods later, in the AutowiredAnnotationBeanPostProcessor.postProcessMe rgedBeanDefinition(), an instance of the InjectionMetadata class for the bean is fetched based on the bean class and not bean definition object, which means that instances of InjectionMetadata are cached per bean class. Therefore despite the fact that two threads cannot enter the same post processing section for the same bean instance, they can enter it for the same bean class and therefore will share the same InjectionMetadata instance. Thus in the next method:

    Code:
    public void checkConfigMembers(RootBeanDefinition beanDefinition) {
      for (Iterator<InjectedElement> it = this.injectedElements.iterator(); it.hasNext();) {
        if (!beanDefinition.isExternallyManagedConfigMember(member))
          beanDefinition.registerExternallyManagedConfigMember(member);
        } else {
          it.remove();
        }
      }
    }
    The it.remove() will potentially throw the exception.

    To summarize the problem: when defining two or more prototype beans based on the same class in a multithreaded environment, a ConcurrentModificationException may be thrown in the InjectionMetadata class.

    Am I right in my analysis?

    Thanks.
Working...
X