From 8d4953d8d6c369862acb3ce169521dceb8f90d7f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 21 Feb 2024 22:45:32 +0100 Subject: [PATCH 1/3] Add test for cleanup after configuration class creation failure See gh-23343 --- .../ConfigurationClassPostProcessorTests.java | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java index 2c3a188ce20..c205ac781ab 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java @@ -35,6 +35,8 @@ import org.springframework.aop.support.AopUtils; import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanDefinitionStoreException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.ObjectProvider; @@ -944,8 +946,8 @@ class ConfigurationClassPostProcessorTests { beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfig.class)); beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class)); new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); - beanFactory.preInstantiateSingletons(); + beanFactory.preInstantiateSingletons(); beanFactory.getBean(ServiceBean.class); } @@ -958,8 +960,8 @@ class ConfigurationClassPostProcessorTests { beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfigWithDefaultMethods.class)); beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class)); new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); - beanFactory.preInstantiateSingletons(); + beanFactory.preInstantiateSingletons(); beanFactory.getBean(ServiceBean.class); } @@ -972,11 +974,25 @@ class ConfigurationClassPostProcessorTests { beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfigWithDefaultMethods.class.getName())); beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class.getName())); new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); - beanFactory.preInstantiateSingletons(); + beanFactory.preInstantiateSingletons(); beanFactory.getBean(ServiceBean.class); } + @Test + void testConfigWithFailingInit() { // gh-23343 + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(beanFactory); + beanFactory.addBeanPostProcessor(bpp); + beanFactory.addBeanPostProcessor(new CommonAnnotationBeanPostProcessor()); + beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfigWithFailingInit.class)); + new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); + + assertThatExceptionOfType(BeanCreationException.class).isThrownBy(beanFactory::preInstantiateSingletons); + assertThat(beanFactory.containsSingleton("configClass")).isFalse(); + assertThat(beanFactory.containsSingleton("provider")).isFalse(); + } + @Test void testCircularDependency() { AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); @@ -985,6 +1001,7 @@ class ConfigurationClassPostProcessorTests { beanFactory.registerBeanDefinition("configClass1", new RootBeanDefinition(A.class)); beanFactory.registerBeanDefinition("configClass2", new RootBeanDefinition(AStrich.class)); new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); + assertThatExceptionOfType(BeanCreationException.class) .isThrownBy(beanFactory::preInstantiateSingletons) .withMessageContaining("Circular reference"); @@ -1768,6 +1785,29 @@ class ConfigurationClassPostProcessorTests { } } + @Configuration + public static class ConcreteConfigWithFailingInit implements DefaultMethodsConfig, BeanFactoryAware { + + private BeanFactory beanFactory; + + @Override + public void setBeanFactory(BeanFactory beanFactory) { + this.beanFactory = beanFactory; + } + + @Bean + @Override + public ServiceBeanProvider provider() { + return new ServiceBeanProvider(); + } + + @PostConstruct + public void validate() { + beanFactory.getBean("provider"); + throw new IllegalStateException(); + } + } + @Primary public static class ServiceBeanProvider { From 7ffeb59b40b1ec6fc3e5fed4a6a5c8850074c118 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 21 Feb 2024 22:45:39 +0100 Subject: [PATCH 2/3] Polishing --- .../factory/support/ConstructorResolver.java | 11 ++-- .../support/DefaultSingletonBeanRegistry.java | 12 ++-- .../annotation/ConfigurationClass.java | 27 ++++----- .../BeanMethodPolymorphismTests.java | 16 +++++- ...mponentScanAnnotationIntegrationTests.java | 57 +++++++++++-------- 5 files changed, 70 insertions(+), 53 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java index 4e7df85fa54..eb08816807e 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -610,13 +610,10 @@ class ConstructorResolver { String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes); throw new BeanCreationException(mbd.getResourceDescription(), beanName, "No matching factory method found on class [" + factoryClass.getName() + "]: " + - (mbd.getFactoryBeanName() != null ? - "factory bean '" + mbd.getFactoryBeanName() + "'; " : "") + + (mbd.getFactoryBeanName() != null ? "factory bean '" + mbd.getFactoryBeanName() + "'; " : "") + "factory method '" + mbd.getFactoryMethodName() + "(" + argDesc + ")'. " + - "Check that a method with the specified name " + - (minNrOfArgs > 0 ? "and arguments " : "") + - "exists and that it is " + - (isStatic ? "static" : "non-static") + "."); + "Check that a method with the specified name " + (minNrOfArgs > 0 ? "and arguments " : "") + + "exists and that it is " + (isStatic ? "static" : "non-static") + "."); } else if (void.class == factoryMethodToUse.getReturnType()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java index 9b189b34312..81e44240497 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -567,16 +567,16 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements */ protected void destroyBean(String beanName, @Nullable DisposableBean bean) { // Trigger destruction of dependent beans first... - Set dependencies; + Set dependentBeanNames; synchronized (this.dependentBeanMap) { // Within full synchronization in order to guarantee a disconnected Set - dependencies = this.dependentBeanMap.remove(beanName); + dependentBeanNames = this.dependentBeanMap.remove(beanName); } - if (dependencies != null) { + if (dependentBeanNames != null) { if (logger.isTraceEnabled()) { - logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies); + logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependentBeanNames); } - for (String dependentBeanName : dependencies) { + for (String dependentBeanName : dependentBeanNames) { destroySingleton(dependentBeanName); } } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java index e462008c7c2..f952bf666ba 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -73,7 +73,6 @@ final class ConfigurationClass { * Create a new {@link ConfigurationClass} with the given name. * @param metadataReader reader used to parse the underlying {@link Class} * @param beanName must not be {@code null} - * @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass) */ ConfigurationClass(MetadataReader metadataReader, String beanName) { Assert.notNull(beanName, "Bean name must not be null"); @@ -87,10 +86,10 @@ final class ConfigurationClass { * using the {@link Import} annotation or automatically processed as a nested * configuration class (if importedBy is not {@code null}). * @param metadataReader reader used to parse the underlying {@link Class} - * @param importedBy the configuration class importing this one or {@code null} + * @param importedBy the configuration class importing this one * @since 3.1.1 */ - ConfigurationClass(MetadataReader metadataReader, @Nullable ConfigurationClass importedBy) { + ConfigurationClass(MetadataReader metadataReader, ConfigurationClass importedBy) { this.metadata = metadataReader.getAnnotationMetadata(); this.resource = metadataReader.getResource(); this.importedBy.add(importedBy); @@ -100,7 +99,6 @@ final class ConfigurationClass { * Create a new {@link ConfigurationClass} with the given name. * @param clazz the underlying {@link Class} to represent * @param beanName name of the {@code @Configuration} class bean - * @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass) */ ConfigurationClass(Class clazz, String beanName) { Assert.notNull(beanName, "Bean name must not be null"); @@ -114,10 +112,10 @@ final class ConfigurationClass { * using the {@link Import} annotation or automatically processed as a nested * configuration class (if imported is {@code true}). * @param clazz the underlying {@link Class} to represent - * @param importedBy the configuration class importing this one (or {@code null}) + * @param importedBy the configuration class importing this one * @since 3.1.1 */ - ConfigurationClass(Class clazz, @Nullable ConfigurationClass importedBy) { + ConfigurationClass(Class clazz, ConfigurationClass importedBy) { this.metadata = AnnotationMetadata.introspect(clazz); this.resource = new DescriptiveResource(clazz.getName()); this.importedBy.add(importedBy); @@ -127,7 +125,6 @@ final class ConfigurationClass { * Create a new {@link ConfigurationClass} with the given name. * @param metadata the metadata for the underlying class to represent * @param beanName name of the {@code @Configuration} class bean - * @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass) */ ConfigurationClass(AnnotationMetadata metadata, String beanName) { Assert.notNull(beanName, "Bean name must not be null"); @@ -149,12 +146,12 @@ final class ConfigurationClass { return ClassUtils.getShortName(getMetadata().getClassName()); } - void setBeanName(String beanName) { + void setBeanName(@Nullable String beanName) { this.beanName = beanName; } @Nullable - public String getBeanName() { + String getBeanName() { return this.beanName; } @@ -164,7 +161,7 @@ final class ConfigurationClass { * @since 3.1.1 * @see #getImportedBy() */ - public boolean isImported() { + boolean isImported() { return !this.importedBy.isEmpty(); } @@ -198,6 +195,10 @@ final class ConfigurationClass { this.importedResources.put(importedResource, readerClass); } + Map> getImportedResources() { + return this.importedResources; + } + void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) { this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata); } @@ -206,10 +207,6 @@ final class ConfigurationClass { return this.importBeanDefinitionRegistrars; } - Map> getImportedResources() { - return this.importedResources; - } - void validate(ProblemReporter problemReporter) { Map attributes = this.metadata.getAnnotationAttributes(Configuration.class.getName()); diff --git a/spring-context/src/test/java/org/springframework/context/annotation/BeanMethodPolymorphismTests.java b/spring-context/src/test/java/org/springframework/context/annotation/BeanMethodPolymorphismTests.java index 773b2345fdd..2d8dee4d538 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/BeanMethodPolymorphismTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/BeanMethodPolymorphismTests.java @@ -29,6 +29,7 @@ import static org.assertj.core.api.Assertions.assertThat; /** * Tests regarding overloading and overriding of bean methods. + * *

Related to SPR-6618. * * @author Chris Beams @@ -41,6 +42,7 @@ public class BeanMethodPolymorphismTests { @Test void beanMethodDetectedOnSuperClass() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class); + assertThat(ctx.getBean("testBean", BaseTestBean.class)).isNotNull(); } @@ -50,6 +52,7 @@ public class BeanMethodPolymorphismTests { ctx.register(OverridingConfig.class); ctx.setAllowBeanDefinitionOverriding(false); ctx.refresh(); + assertThat(ctx.getDefaultListableBeanFactory().containsSingleton("testBean")).isFalse(); assertThat(ctx.getBean("testBean", BaseTestBean.class).toString()).isEqualTo("overridden"); assertThat(ctx.getDefaultListableBeanFactory().containsSingleton("testBean")).isTrue(); @@ -61,6 +64,7 @@ public class BeanMethodPolymorphismTests { ctx.registerBeanDefinition("config", new RootBeanDefinition(OverridingConfig.class.getName())); ctx.setAllowBeanDefinitionOverriding(false); ctx.refresh(); + assertThat(ctx.getDefaultListableBeanFactory().containsSingleton("testBean")).isFalse(); assertThat(ctx.getBean("testBean", BaseTestBean.class).toString()).isEqualTo("overridden"); assertThat(ctx.getDefaultListableBeanFactory().containsSingleton("testBean")).isTrue(); @@ -72,6 +76,7 @@ public class BeanMethodPolymorphismTests { ctx.register(NarrowedOverridingConfig.class); ctx.setAllowBeanDefinitionOverriding(false); ctx.refresh(); + assertThat(ctx.getDefaultListableBeanFactory().containsSingleton("testBean")).isFalse(); assertThat(ctx.getBean("testBean", BaseTestBean.class).toString()).isEqualTo("overridden"); assertThat(ctx.getDefaultListableBeanFactory().containsSingleton("testBean")).isTrue(); @@ -83,6 +88,7 @@ public class BeanMethodPolymorphismTests { ctx.registerBeanDefinition("config", new RootBeanDefinition(NarrowedOverridingConfig.class.getName())); ctx.setAllowBeanDefinitionOverriding(false); ctx.refresh(); + assertThat(ctx.getDefaultListableBeanFactory().containsSingleton("testBean")).isFalse(); assertThat(ctx.getBean("testBean", BaseTestBean.class).toString()).isEqualTo("overridden"); assertThat(ctx.getDefaultListableBeanFactory().containsSingleton("testBean")).isTrue(); @@ -94,6 +100,7 @@ public class BeanMethodPolymorphismTests { ctx.register(ConfigWithOverloading.class); ctx.setAllowBeanDefinitionOverriding(false); ctx.refresh(); + assertThat(ctx.getBean(String.class)).isEqualTo("regular"); } @@ -104,6 +111,7 @@ public class BeanMethodPolymorphismTests { ctx.getDefaultListableBeanFactory().registerSingleton("anInt", 5); ctx.setAllowBeanDefinitionOverriding(false); ctx.refresh(); + assertThat(ctx.getBean(String.class)).isEqualTo("overloaded5"); } @@ -113,6 +121,7 @@ public class BeanMethodPolymorphismTests { ctx.register(ConfigWithOverloadingAndAdditionalMetadata.class); ctx.setAllowBeanDefinitionOverriding(false); ctx.refresh(); + assertThat(ctx.getDefaultListableBeanFactory().containsSingleton("aString")).isFalse(); assertThat(ctx.getBean(String.class)).isEqualTo("regular"); assertThat(ctx.getDefaultListableBeanFactory().containsSingleton("aString")).isTrue(); @@ -125,6 +134,7 @@ public class BeanMethodPolymorphismTests { ctx.getDefaultListableBeanFactory().registerSingleton("anInt", 5); ctx.setAllowBeanDefinitionOverriding(false); ctx.refresh(); + assertThat(ctx.getDefaultListableBeanFactory().containsSingleton("aString")).isFalse(); assertThat(ctx.getBean(String.class)).isEqualTo("overloaded5"); assertThat(ctx.getDefaultListableBeanFactory().containsSingleton("aString")).isTrue(); @@ -136,18 +146,19 @@ public class BeanMethodPolymorphismTests { ctx.register(SubConfig.class); ctx.setAllowBeanDefinitionOverriding(false); ctx.refresh(); + assertThat(ctx.getDefaultListableBeanFactory().containsSingleton("aString")).isFalse(); assertThat(ctx.getBean(String.class)).isEqualTo("overloaded5"); assertThat(ctx.getDefaultListableBeanFactory().containsSingleton("aString")).isTrue(); } - // SPR-11025 - @Test + @Test // SPR-11025 void beanMethodOverloadingWithInheritanceAndList() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(SubConfigWithList.class); ctx.setAllowBeanDefinitionOverriding(false); ctx.refresh(); + assertThat(ctx.getDefaultListableBeanFactory().containsSingleton("aString")).isFalse(); assertThat(ctx.getBean(String.class)).isEqualTo("overloaded5"); assertThat(ctx.getDefaultListableBeanFactory().containsSingleton("aString")).isTrue(); @@ -161,6 +172,7 @@ public class BeanMethodPolymorphismTests { @Test void beanMethodShadowing() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ShadowConfig.class); + assertThat(ctx.getBean(String.class)).isEqualTo("shadow"); } diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java index c3aeec55275..0603f4d6193 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -80,15 +80,16 @@ class ComponentScanAnnotationIntegrationTests { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.scan(example.scannable.PackageMarker.class.getPackage().getName()); ctx.refresh(); + assertContextContainsBean(ctx, "fooServiceImpl"); } @Test void viaContextRegistration() { - AnnotationConfigApplicationContext ctx = - new AnnotationConfigApplicationContext(ComponentScanAnnotatedConfig.class); + ApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanAnnotatedConfig.class); ctx.getBean(ComponentScanAnnotatedConfig.class); ctx.getBean(TestBean.class); + assertThat(ctx.containsBeanDefinition("componentScanAnnotatedConfig")).as("config class bean not found").isTrue(); assertThat(ctx.containsBean("fooServiceImpl")).as("@ComponentScan annotated @Configuration class registered directly against " + "AnnotationConfigApplicationContext did not trigger component scanning as expected").isTrue(); @@ -96,10 +97,10 @@ class ComponentScanAnnotationIntegrationTests { @Test void viaContextRegistration_WithValueAttribute() { - AnnotationConfigApplicationContext ctx = - new AnnotationConfigApplicationContext(ComponentScanAnnotatedConfig_WithValueAttribute.class); + ApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanAnnotatedConfig_WithValueAttribute.class); ctx.getBean(ComponentScanAnnotatedConfig_WithValueAttribute.class); ctx.getBean(TestBean.class); + assertThat(ctx.containsBeanDefinition("componentScanAnnotatedConfig_WithValueAttribute")).as("config class bean not found").isTrue(); assertThat(ctx.containsBean("fooServiceImpl")).as("@ComponentScan annotated @Configuration class registered directly against " + "AnnotationConfigApplicationContext did not trigger component scanning as expected").isTrue(); @@ -107,9 +108,9 @@ class ComponentScanAnnotationIntegrationTests { @Test void viaContextRegistration_FromPackageOfConfigClass() { - AnnotationConfigApplicationContext ctx = - new AnnotationConfigApplicationContext(ComponentScanAnnotatedConfigWithImplicitBasePackage.class); + ApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanAnnotatedConfigWithImplicitBasePackage.class); ctx.getBean(ComponentScanAnnotatedConfigWithImplicitBasePackage.class); + assertThat(ctx.containsBeanDefinition("componentScanAnnotatedConfigWithImplicitBasePackage")).as("config class bean not found").isTrue(); assertThat(ctx.containsBean("scannedComponent")).as("@ComponentScan annotated @Configuration class registered directly against " + "AnnotationConfigApplicationContext did not trigger component scanning as expected").isTrue(); @@ -118,11 +119,12 @@ class ComponentScanAnnotationIntegrationTests { @Test void viaContextRegistration_WithComposedAnnotation() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ComposedAnnotationConfig.class); + ApplicationContext ctx = new AnnotationConfigApplicationContext(ComposedAnnotationConfig.class); ctx.getBean(ComposedAnnotationConfig.class); ctx.getBean(SimpleComponent.class); ctx.getBean(ClassWithNestedComponents.NestedComponent.class); ctx.getBean(ClassWithNestedComponents.OtherNestedComponent.class); + assertThat(ctx.containsBeanDefinition("componentScanAnnotationIntegrationTests.ComposedAnnotationConfig")).as("config class bean not found").isTrue(); assertThat(ctx.containsBean("simpleComponent")).as("@ComponentScan annotated @Configuration class registered directly against " + "AnnotationConfigApplicationContext did not trigger component scanning as expected").isTrue(); @@ -132,6 +134,7 @@ class ComponentScanAnnotationIntegrationTests { void multipleComposedComponentScanAnnotations() { // gh-30941 ApplicationContext ctx = new AnnotationConfigApplicationContext(MultipleComposedAnnotationsConfig.class); ctx.getBean(MultipleComposedAnnotationsConfig.class); + assertContextContainsBean(ctx, "componentScanAnnotationIntegrationTests.MultipleComposedAnnotationsConfig"); assertContextContainsBean(ctx, "simpleComponent"); assertContextContainsBean(ctx, "barComponent"); @@ -143,7 +146,6 @@ class ComponentScanAnnotationIntegrationTests { assertContextContainsBean(ctx, "componentScanAnnotationIntegrationTests.LocalAnnotationOverridesMultipleMetaAnnotationsConfig"); assertContextContainsBean(ctx, "barComponent"); - assertContextDoesNotContainBean(ctx, "simpleComponent"); assertContextDoesNotContainBean(ctx, "configurableComponent"); } @@ -154,7 +156,6 @@ class ComponentScanAnnotationIntegrationTests { assertContextContainsBean(ctx, "componentScanAnnotationIntegrationTests.LocalAnnotationOverridesMultipleComposedAnnotationsConfig"); assertContextContainsBean(ctx, "barComponent"); - assertContextDoesNotContainBean(ctx, "simpleComponent"); assertContextDoesNotContainBean(ctx, "configurableComponent"); } @@ -166,7 +167,6 @@ class ComponentScanAnnotationIntegrationTests { assertContextContainsBean(ctx, "componentScanAnnotationIntegrationTests.LocalRepeatedAnnotationsOverrideComposedAnnotationsConfig"); assertContextContainsBean(ctx, "barComponent"); assertContextContainsBean(ctx, "configurableComponent"); - assertContextDoesNotContainBean(ctx, "simpleComponent"); } @@ -177,7 +177,6 @@ class ComponentScanAnnotationIntegrationTests { assertContextContainsBean(ctx, "componentScanAnnotationIntegrationTests.LocalRepeatedAnnotationsInContainerOverrideComposedAnnotationsConfig"); assertContextContainsBean(ctx, "barComponent"); assertContextContainsBean(ctx, "configurableComponent"); - assertContextDoesNotContainBean(ctx, "simpleComponent"); } @@ -188,10 +187,12 @@ class ComponentScanAnnotationIntegrationTests { genericBeanDefinition(ComponentScanAnnotatedConfig.class).getBeanDefinition()); bf.registerBeanDefinition("configurationClassPostProcessor", genericBeanDefinition(ConfigurationClassPostProcessor.class).getBeanDefinition()); + GenericApplicationContext ctx = new GenericApplicationContext(bf); ctx.refresh(); ctx.getBean(ComponentScanAnnotatedConfig.class); ctx.getBean(TestBean.class); + assertThat(ctx.containsBeanDefinition("componentScanAnnotatedConfig")).as("config class bean not found").isTrue(); assertThat(ctx.containsBean("fooServiceImpl")).as("@ComponentScan annotated @Configuration class registered as bean " + "definition did not trigger component scanning as expected").isTrue(); @@ -206,7 +207,8 @@ class ComponentScanAnnotationIntegrationTests { @Test void withScopeResolver() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanWithScopeResolver.class); + ApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanWithScopeResolver.class); + // custom scope annotation makes the bean prototype scoped. subsequent calls // to getBean should return distinct instances. assertThat(ctx.getBean(CustomScopeAnnotationBean.class)).isNotSameAs(ctx.getBean(CustomScopeAnnotationBean.class)); @@ -215,7 +217,8 @@ class ComponentScanAnnotationIntegrationTests { @Test void multiComponentScan() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MultiComponentScan.class); + ApplicationContext ctx = new AnnotationConfigApplicationContext(MultiComponentScan.class); + assertThat(ctx.getBean(CustomScopeAnnotationBean.class)).isNotSameAs(ctx.getBean(CustomScopeAnnotationBean.class)); assertContextContainsBean(ctx, "scannedComponent"); } @@ -223,14 +226,16 @@ class ComponentScanAnnotationIntegrationTests { @Test void withCustomTypeFilter() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanWithCustomTypeFilter.class); - assertThat(ctx.getDefaultListableBeanFactory().containsSingleton("componentScanParserTests.KustomAnnotationAutowiredBean")).isFalse(); + + assertThat(ctx.getBeanFactory().containsSingleton("componentScanParserTests.KustomAnnotationAutowiredBean")).isFalse(); KustomAnnotationAutowiredBean testBean = ctx.getBean("componentScanParserTests.KustomAnnotationAutowiredBean", KustomAnnotationAutowiredBean.class); assertThat(testBean.getDependency()).isNotNull(); } @Test void withAwareTypeFilter() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanWithAwareTypeFilter.class); + ApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanWithAwareTypeFilter.class); + assertThat(ctx.getEnvironment().matchesProfiles("the-filter-ran")).isTrue(); } @@ -240,6 +245,7 @@ class ComponentScanAnnotationIntegrationTests { ctx.register(ComponentScanWithScopedProxy.class); ctx.getBeanFactory().registerScope("myScope", new SimpleMapScope()); ctx.refresh(); + // should cast to the interface FooService bean = (FooService) ctx.getBean("scopedProxyTestBean"); // should be dynamic proxy @@ -257,6 +263,7 @@ class ComponentScanAnnotationIntegrationTests { ctx.register(ComponentScanWithScopedProxyThroughRegex.class); ctx.getBeanFactory().registerScope("myScope", new SimpleMapScope()); ctx.refresh(); + // should cast to the interface FooService bean = (FooService) ctx.getBean("scopedProxyTestBean"); // should be dynamic proxy @@ -269,6 +276,7 @@ class ComponentScanAnnotationIntegrationTests { ctx.register(ComponentScanWithScopedProxyThroughAspectJPattern.class); ctx.getBeanFactory().registerScope("myScope", new SimpleMapScope()); ctx.refresh(); + // should cast to the interface FooService bean = (FooService) ctx.getBean("scopedProxyTestBean"); // should be dynamic proxy @@ -277,16 +285,16 @@ class ComponentScanAnnotationIntegrationTests { @Test void withMultipleAnnotationIncludeFilters1() { - AnnotationConfigApplicationContext ctx = - new AnnotationConfigApplicationContext(ComponentScanWithMultipleAnnotationIncludeFilters1.class); + ApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanWithMultipleAnnotationIncludeFilters1.class); + ctx.getBean(DefaultNamedComponent.class); // @CustomStereotype-annotated ctx.getBean(MessageBean.class); // @CustomComponent-annotated } @Test void withMultipleAnnotationIncludeFilters2() { - AnnotationConfigApplicationContext ctx = - new AnnotationConfigApplicationContext(ComponentScanWithMultipleAnnotationIncludeFilters2.class); + ApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanWithMultipleAnnotationIncludeFilters2.class); + ctx.getBean(DefaultNamedComponent.class); // @CustomStereotype-annotated ctx.getBean(MessageBean.class); // @CustomComponent-annotated } @@ -296,25 +304,28 @@ class ComponentScanAnnotationIntegrationTests { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(ComponentScanWithMultipleAnnotationIncludeFilters3.class); ctx.refresh(); + assertThat(ctx.getBean(DefaultNamedComponent.class).toString()).isEqualTo("overridden"); } @Test void withBeanMethodOverrideAndGeneralOverridingDisabled() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); - ctx.getDefaultListableBeanFactory().setAllowBeanDefinitionOverriding(false); + ctx.setAllowBeanDefinitionOverriding(false); ctx.register(ComponentScanWithMultipleAnnotationIncludeFilters3.class); ctx.refresh(); + assertThat(ctx.getBean(DefaultNamedComponent.class).toString()).isEqualTo("overridden"); } @Test void withBasePackagesAndValueAlias() { - AnnotationConfigApplicationContext ctx = - new AnnotationConfigApplicationContext(ComponentScanWithBasePackagesAndValueAlias.class); + ApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanWithBasePackagesAndValueAlias.class); + assertContextContainsBean(ctx, "fooServiceImpl"); } + private static void assertContextContainsBean(ApplicationContext ctx, String beanName) { assertThat(ctx.containsBean(beanName)).as("context should contain bean " + beanName).isTrue(); } From 41433d445e42e9c12f136c2e725a54855ba008c9 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 21 Feb 2024 22:58:42 +0100 Subject: [PATCH 3/3] Polishing --- .../beans/factory/support/ConstructorResolver.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java index eb08816807e..4670ac1f829 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java @@ -1193,8 +1193,7 @@ class ConstructorResolver { } private Predicate isAssignable(ResolvableType valueType) { - return parameterType -> (valueType == ResolvableType.NONE - || parameterType.isAssignableFrom(valueType)); + return parameterType -> (valueType == ResolvableType.NONE || parameterType.isAssignableFrom(valueType)); } private ResolvableType extractElementType(ResolvableType parameterType) {