diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index e010d1483f6..95bd2e43fa8 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -606,9 +606,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac if (ex instanceof BeanCreationException bce && beanName.equals(bce.getBeanName())) { throw bce; } - else { - throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex); - } + throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex); } if (earlySingletonExposure) { @@ -1246,8 +1244,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac instance = obtainInstanceFromSupplier(supplier, beanName, mbd); } catch (Throwable ex) { - if (ex instanceof BeansException beansException) { - throw beansException; + if (ex instanceof BeanCreationException bce && beanName.equals(bce.getBeanName())) { + throw bce; } throw new BeanCreationException(beanName, "Instantiation of supplied bean failed", ex); } diff --git a/spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java b/spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java index 3a39f0620c0..80798fed535 100644 --- a/spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java +++ b/spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java @@ -30,6 +30,7 @@ import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.BeanCurrentlyInCreationException; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.NoUniqueBeanDefinitionException; @@ -41,14 +42,17 @@ import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostP import org.springframework.beans.factory.config.TypedStringValue; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionOverrideException; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import org.springframework.context.testfixture.beans.factory.CircularBeanRegistrar; import org.springframework.context.testfixture.beans.factory.ConditionalBeanRegistrar; import org.springframework.context.testfixture.beans.factory.ImportAwareBeanRegistrar; +import org.springframework.context.testfixture.beans.factory.OverridingBeanRegistrar; import org.springframework.context.testfixture.beans.factory.SampleBeanRegistrar; import org.springframework.core.DecoratingProxy; import org.springframework.core.env.ConfigurableEnvironment; @@ -639,6 +643,22 @@ class GenericApplicationContextTests { assertThat(context.getBean(SampleBeanRegistrar.Bar.class).foo()).isEqualTo(context.getBean(SampleBeanRegistrar.Foo.class)); } + @Test + void beanRegistrarWithCircularReference() { + GenericApplicationContext context = new GenericApplicationContext(); + context.register(new CircularBeanRegistrar()); + assertThatExceptionOfType(BeanCreationException.class).isThrownBy(context::refresh) + .withRootCauseInstanceOf(BeanCurrentlyInCreationException.class); + } + + @Test + void beanRegistrarWithDefinitionOverride() { + GenericApplicationContext context = new GenericApplicationContext(); + context.setAllowBeanDefinitionOverriding(false); + assertThatExceptionOfType(BeanDefinitionOverrideException.class).isThrownBy( + () -> context.register(new OverridingBeanRegistrar())); + } + @Test void importAwareBeanRegistrar() { GenericApplicationContext context = new GenericApplicationContext(); diff --git a/spring-context/src/testFixtures/java/org/springframework/context/testfixture/beans/factory/CircularBeanRegistrar.java b/spring-context/src/testFixtures/java/org/springframework/context/testfixture/beans/factory/CircularBeanRegistrar.java new file mode 100644 index 00000000000..76aa25295c6 --- /dev/null +++ b/spring-context/src/testFixtures/java/org/springframework/context/testfixture/beans/factory/CircularBeanRegistrar.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.context.testfixture.beans.factory; + +import jakarta.annotation.PostConstruct; + +import org.springframework.beans.factory.BeanRegistrar; +import org.springframework.beans.factory.BeanRegistry; +import org.springframework.core.env.Environment; + +public class CircularBeanRegistrar implements BeanRegistrar { + + @Override + public void register(BeanRegistry registry, Environment env) { + registry.registerBean("foo", Foo.class, spec -> spec.supplier(context -> new Foo(context.bean(Bar.class)))); + registry.registerAlias("foo", "fooAlias"); + registry.registerBean("bar", Bar.class, spec -> spec + .prototype() + .lazyInit() + .description("Custom description") + .supplier(context -> new Bar(context.bean(Foo.class)))); + if (env.matchesProfiles("baz")) { + registry.registerBean(Baz.class, spec -> spec + .supplier(context -> new Baz("Hello World!"))); + } + registry.registerBean(Init.class); + } + + public record Foo(Bar bar) {} + + public record Bar(Foo foo) {} + + public record Baz(String message) {} + + public static class Init { + + public boolean initialized = false; + + @PostConstruct + public void postConstruct() { + initialized = true; + } + } + +} diff --git a/spring-context/src/testFixtures/java/org/springframework/context/testfixture/beans/factory/OverridingBeanRegistrar.java b/spring-context/src/testFixtures/java/org/springframework/context/testfixture/beans/factory/OverridingBeanRegistrar.java new file mode 100644 index 00000000000..318e31f6ed5 --- /dev/null +++ b/spring-context/src/testFixtures/java/org/springframework/context/testfixture/beans/factory/OverridingBeanRegistrar.java @@ -0,0 +1,33 @@ +/* + * Copyright 2002-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.context.testfixture.beans.factory; + +import org.springframework.beans.factory.BeanRegistrar; +import org.springframework.beans.factory.BeanRegistry; +import org.springframework.core.env.Environment; + +public class OverridingBeanRegistrar implements BeanRegistrar { + + @Override + public void register(BeanRegistry registry, Environment env) { + registry.registerBean("foo", Foo.class); + registry.registerBean("foo", Foo.class); + } + + public record Foo() {} + +}