diff --git a/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java b/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java index d77e4377c63..32f68f2c7b1 100644 --- a/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java +++ b/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java @@ -82,13 +82,19 @@ public abstract class ScopedProxyUtils { // Copy autowire settings from original bean definition. proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate()); proxyDefinition.setPrimary(targetDefinition.isPrimary()); + proxyDefinition.setFallback(targetDefinition.isFallback()); if (targetDefinition instanceof AbstractBeanDefinition abd) { + proxyDefinition.setDefaultCandidate(abd.isDefaultCandidate()); proxyDefinition.copyQualifiersFrom(abd); } // The target bean should be ignored in favor of the scoped proxy. targetDefinition.setAutowireCandidate(false); targetDefinition.setPrimary(false); + targetDefinition.setFallback(false); + if (targetDefinition instanceof AbstractBeanDefinition abd) { + abd.setDefaultCandidate(false); + } // Register the target bean as separate bean in the factory. registry.registerBeanDefinition(targetBeanName, targetDefinition); diff --git a/spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyUtilsTests.java b/spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyUtilsTests.java index ff3299cd4a5..de5e76db2b7 100644 --- a/spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyUtilsTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyUtilsTests.java @@ -18,6 +18,15 @@ package org.springframework.aop.scope; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.AutowireCandidateQualifier; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.GenericBeanDefinition; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -25,6 +34,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException * Tests for {@link ScopedProxyUtils}. * * @author Sam Brannen + * @author Juergen Hoeller * @since 5.1.10 */ class ScopedProxyUtilsTests { @@ -53,15 +63,79 @@ class ScopedProxyUtilsTests { @Test void getOriginalBeanNameForNullTargetBean() { assertThatIllegalArgumentException() - .isThrownBy(() -> ScopedProxyUtils.getOriginalBeanName(null)) - .withMessage("bean name 'null' does not refer to the target of a scoped proxy"); + .isThrownBy(() -> ScopedProxyUtils.getOriginalBeanName(null)) + .withMessage("bean name 'null' does not refer to the target of a scoped proxy"); } @Test void getOriginalBeanNameForNonScopedTarget() { assertThatIllegalArgumentException() - .isThrownBy(() -> ScopedProxyUtils.getOriginalBeanName("myBean")) - .withMessage("bean name 'myBean' does not refer to the target of a scoped proxy"); + .isThrownBy(() -> ScopedProxyUtils.getOriginalBeanName("myBean")) + .withMessage("bean name 'myBean' does not refer to the target of a scoped proxy"); + } + + @Test + void createScopedProxyTargetAppliesAutowireSettingsToProxyBeanDefinition() { + AbstractBeanDefinition targetDefinition = new GenericBeanDefinition(); + // Opposite of defaults + targetDefinition.setAutowireCandidate(false); + targetDefinition.setDefaultCandidate(false); + targetDefinition.setPrimary(true); + targetDefinition.setFallback(true); + + BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry(); + BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy( + new BeanDefinitionHolder(targetDefinition, "myBean"), registry, false); + AbstractBeanDefinition proxyBeanDefinition = (AbstractBeanDefinition) proxyHolder.getBeanDefinition(); + + assertThat(proxyBeanDefinition.isAutowireCandidate()).isFalse(); + assertThat(proxyBeanDefinition.isDefaultCandidate()).isFalse(); + assertThat(proxyBeanDefinition.isPrimary()).isTrue(); + assertThat(proxyBeanDefinition.isFallback()).isTrue(); + } + + @Test + void createScopedProxyTargetAppliesBeanAttributesToProxyBeanDefinition() { + GenericBeanDefinition targetDefinition = new GenericBeanDefinition(); + // Opposite of defaults + targetDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + targetDefinition.setSource("theSource"); + targetDefinition.addQualifier(new AutowireCandidateQualifier("myQualifier")); + + BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry(); + BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy( + new BeanDefinitionHolder(targetDefinition, "myBean"), registry, false); + BeanDefinition proxyBeanDefinition = proxyHolder.getBeanDefinition(); + + assertThat(proxyBeanDefinition.getRole()).isEqualTo(BeanDefinition.ROLE_INFRASTRUCTURE); + assertThat(proxyBeanDefinition).isInstanceOf(RootBeanDefinition.class); + assertThat(proxyBeanDefinition.getPropertyValues()).hasSize(2); + assertThat(proxyBeanDefinition.getPropertyValues().get("proxyTargetClass")).isEqualTo(false); + assertThat(proxyBeanDefinition.getPropertyValues().get("targetBeanName")).isEqualTo( + ScopedProxyUtils.getTargetBeanName("myBean")); + + RootBeanDefinition rootBeanDefinition = (RootBeanDefinition) proxyBeanDefinition; + assertThat(rootBeanDefinition.getQualifiers()).hasSize(1); + assertThat(rootBeanDefinition.hasQualifier("myQualifier")).isTrue(); + assertThat(rootBeanDefinition.getSource()).isEqualTo("theSource"); + } + + @Test + void createScopedProxyTargetCleansAutowireSettingsInTargetDefinition() { + AbstractBeanDefinition targetDefinition = new GenericBeanDefinition(); + targetDefinition.setAutowireCandidate(true); + targetDefinition.setDefaultCandidate(true); + targetDefinition.setPrimary(true); + targetDefinition.setFallback(true); + + BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry(); + ScopedProxyUtils.createScopedProxy( + new BeanDefinitionHolder(targetDefinition, "myBean"), registry, false); + + assertThat(targetDefinition.isAutowireCandidate()).isFalse(); + assertThat(targetDefinition.isDefaultCandidate()).isFalse(); + assertThat(targetDefinition.isPrimary()).isFalse(); + assertThat(targetDefinition.isFallback()).isFalse(); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index e8c28b6cd5d..a223d2261aa 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -1477,7 +1477,10 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @Override public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException { super.registerSingleton(beanName, singletonObject); + updateManualSingletonNames(set -> set.add(beanName), set -> !this.beanDefinitionMap.containsKey(beanName)); + this.allBeanNamesByType.remove(Object.class); + this.singletonBeanNamesByType.remove(Object.class); } @Override diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index 2e3115cabb8..a582e69a286 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -87,6 +87,7 @@ import org.springframework.core.convert.support.GenericConversionService; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.core.testfixture.io.SerializationTestUtils; +import org.springframework.util.StringUtils; import org.springframework.util.StringValueResolver; import static org.assertj.core.api.Assertions.assertThat; @@ -3232,6 +3233,10 @@ class DefaultListableBeanFactoryTests { assertThat(lbf.getBeanNamesForType(DerivedTestBean.class)).containsExactly("bd1"); assertThat(lbf.getBeanNamesForType(NestedTestBean.class)).isSameAs(nestedBeanNames); assertThat(lbf.getBeanNamesForType(Object.class)).isSameAs(allBeanNames); + + lbf.registerSingleton("bd3", new Object()); + assertThat(lbf.getBeanNamesForType(NestedTestBean.class)).isSameAs(nestedBeanNames); + assertThat(lbf.getBeanNamesForType(Object.class)).containsExactly(StringUtils.addStringToArray(allBeanNames, "bd3")); }