Skip to content

BeanCurrentlyInCreationException is thrown when multiple threads simultaneously try to create a FactoryBean #33972

Closed
@dt-mafe

Description

@dt-mafe

After upgrading to spring boot 3.4 and spring 6.2.0 we sometimes see a BeanCurrentlyInCreationException in our logs after application startup.
Looking into it, this seems to be happening when multiple threads simultaneously first create a FactoryBean (in our application we are using spring mvc and this happens when multiple concurrent http requests happen after startup where a BeanCurrentlyInCreationException is thrown for a request scoped bean).

This was working fine in spring 6.1.15 and is broken in 6.2.0.

From a quick look, I would suspect that this is related to changes in this issue where locking in FactoryBeanRegistrySupport was removed.

The following is an example test (inspired by the example of this issue) which runs without any exception in 6.1.15 but does throw an exception in 6.2.0

package org.springframework.beans.factory.xml;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.support.StaticApplicationContext;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

class BeanFactoryRaceConditionTest {

	private final ExecutorService executorService = Executors.newFixedThreadPool(10);

	@Test
	void testRaceCondition() {
		StaticApplicationContext applicationContext = new StaticApplicationContext();
		applicationContext.registerSingleton("book", BookFactory.class);
		List<Future<?>> allFutures = new ArrayList<>();
		for (int i = 0; i < 10; i++) {
			var future = executorService.submit(() -> {
				for (int j = 0; j < 1000; j++) {
					try {
						Book book = applicationContext.getBean(Book.class);
						Assertions.assertThat(book).isNotNull();
					} catch (BeanCurrentlyInCreationException e) {
						throw new RuntimeException(e);
					}
				}
			});
			allFutures.add(future);
		}
		Assertions.assertThatCode(() -> {
			for (Future<?> future : allFutures) {
				future.get();
			}
		}).doesNotThrowAnyException();
	}

	static class Book {

	}

	static class BookFactory implements FactoryBean<Book> {

		@Override
		public Book getObject() {
			return new Book();
		}

		@Override
		public Class<?> getObjectType() {
			return Book.class;
		}
	}
}

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