From 356ffe578f10390d45a0574d597ebea723fdcdbe Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Mon, 22 Jun 2026 10:28:03 +0800 Subject: [PATCH] Improve JpaBaseConfiguration to resolve beans lazily to avoid circular reference See GH-50797 and GH-50749 Signed-off-by: Yanming Zhou --- .../HibernateJpaAutoConfigurationTests.java | 18 ++++++++++++++++++ .../autoconfigure/JpaBaseConfiguration.java | 13 ++++++++----- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/module/spring-boot-hibernate/src/test/java/org/springframework/boot/hibernate/autoconfigure/HibernateJpaAutoConfigurationTests.java b/module/spring-boot-hibernate/src/test/java/org/springframework/boot/hibernate/autoconfigure/HibernateJpaAutoConfigurationTests.java index 5bbb2992cf15..d4e68fcfcb29 100644 --- a/module/spring-boot-hibernate/src/test/java/org/springframework/boot/hibernate/autoconfigure/HibernateJpaAutoConfigurationTests.java +++ b/module/spring-boot-hibernate/src/test/java/org/springframework/boot/hibernate/autoconfigure/HibernateJpaAutoConfigurationTests.java @@ -1017,6 +1017,15 @@ void whenSpringJpaGenerateDdlIsTrueSpringJpaHibernateDdlAutoIsCreateAndJakartaSc .run((context) -> assertThat(tablesFrom(context)).doesNotContain("CITY")); } + @Test // GH-50797 + void circularReference() { + this.contextRunner.withUserConfiguration(CircularReferenceConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(DataSource.class); + assertThat(context).hasSingleBean(JpaTransactionManager.class); + assertThat(context).hasSingleBean(EntityManagerFactory.class); + }); + } + private List tablesFrom(AssertableApplicationContext context) { DataSource dataSource = context.getBean(DataSource.class); JdbcTemplate jdbc = new JdbcTemplate(dataSource); @@ -1474,4 +1483,13 @@ SimpleAsyncTaskExecutor applicationTaskExecutor() { } + @Configuration + static class CircularReferenceConfiguration extends MultipleAsyncTaskExecutorsConfiguration { + + CircularReferenceConfiguration(PlatformTransactionManager transactionManager) { + + } + + } + } diff --git a/module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa/autoconfigure/JpaBaseConfiguration.java b/module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa/autoconfigure/JpaBaseConfiguration.java index 726c06f9ae99..3ca909d0ce3a 100644 --- a/module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa/autoconfigure/JpaBaseConfiguration.java +++ b/module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa/autoconfigure/JpaBaseConfiguration.java @@ -29,6 +29,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.AutoConfigurationPackages; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty; @@ -124,8 +125,9 @@ public JpaVendorAdapter jpaVendorAdapter() { public EntityManagerFactoryBuilder entityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter, ObjectProvider persistenceUnitManager, ObjectProvider customizers, - Map taskExecutors) { - @Nullable AsyncTaskExecutor bootstrapExecutor = determineBootstrapExecutor(taskExecutors); + ObjectProvider taskExecutors, + @Qualifier(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME) ObjectProvider applicationTaskExecutor) { + @Nullable AsyncTaskExecutor bootstrapExecutor = determineBootstrapExecutor(taskExecutors, applicationTaskExecutor); EntityManagerFactoryBuilder builder = new EntityManagerFactoryBuilder(jpaVendorAdapter, this::buildJpaProperties, persistenceUnitManager.getIfAvailable(), null, bootstrapExecutor); if (this.properties.getBootstrap() == Bootstrap.ASYNC) { @@ -136,9 +138,10 @@ public EntityManagerFactoryBuilder entityManagerFactoryBuilder(JpaVendorAdapter return builder; } - private @Nullable AsyncTaskExecutor determineBootstrapExecutor(Map taskExecutors) { - return (taskExecutors.size() == 1) ? taskExecutors.values().iterator().next() - : taskExecutors.get(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME); + private @Nullable AsyncTaskExecutor determineBootstrapExecutor(ObjectProvider taskExecutors, + ObjectProvider applicationTaskExecutor) { + @Nullable AsyncTaskExecutor asyncTaskExecutor = taskExecutors.getIfUnique(); + return (asyncTaskExecutor != null) ? asyncTaskExecutor : applicationTaskExecutor.getIfAvailable(); } private Map buildJpaProperties(DataSource dataSource) {