Skip to content

BeanCurrentlyInCreationException is thrown when multiple threads get a bean from StaticApplicationContext's Bean Factory #33463

Closed
@mt-ocado

Description

@mt-ocado

Affects: 6.2.0-M7

After updating to the latest milestone version (6.2.0-M7), an org.springframework.beans.factory.BeanCurrentlyInCreationException is thrown when getting a bean from beanFactory of StaticApplicationContext. Race conditions take place when multiple threads are involved.
Apparently, DefaultSingletonBeanRegistry#getSingleton(String beanName, ObjectFactory<?> singletonFactory) is the method where the following scenario takes place:

  1. beforeSingletonCreation(beanName) is called in Thread A,
  2. beforeSingletonCreation(beanName) is called in Thread B,
  3. Because afterSingletonCreation(String beanName) has not yet been called in Thread A, the above step will cause race condition and throwing of BeanCurrentlyInCreationException.

Notes

  • In version 6.1.11 the exception is not raised.
  • When using DefaultSingletonBeanRegistry directly instead of through StaticApplicationContext, then no exception is thrown.

Minimal example (might require several runs)

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.support.StaticApplicationContext;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;

class BeanFactoryRaceConditionTest {
    
    private final ExecutorService executorService = Executors.newFixedThreadPool(10);
    
    @Test
    void testRaceCondition() {
        StaticApplicationContext applicationContext = new StaticApplicationContext();
        applicationContext.registerSingleton("book", Book.class);

        BeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();

        for (int i = 0; i < 10; i++) {
            executorService.execute(() -> {
                for (int j = 0; j < 1000; j++) {
                    beanFactory.getBean("book");
                }
            });
        }
        assertThrows(BeanCurrentlyInCreationException.class, () -> {
            for (int i = 0; i < 1000; i++) {
                beanFactory.getBean("book");
            }
        });
    }

    @Test
    void testNoRaceCondition() {
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        beanFactory.registerSingleton("book", Book.class);

        for (int i = 0; i < 10; i++) {
            executorService.execute(() -> {
                for (int j = 0; j < 1000; j++) {
                    beanFactory.getBean("book");
                }
            });
        }

        assertDoesNotThrow(()->{
            for (int i = 0; i < 1000; i++) {
                beanFactory.getBean("book");
            }
        });
    }

    static class Book {
    }
}

Traces for testRaceCondition

Exception in thread "pool-1-thread-5" org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'book': Requested bean is currently in creation: Is there an unresolvable circular reference or an asynchronous initialization dependency?
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:424)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:288)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:334)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	at com.example.demo.BeanFactoryRaceConditionTest.lambda$testRaceCondition$0(BeanFactoryRaceConditionTest.java:23)

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)type: regressionA bug that is also a regression

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions