Skip to content

Injecting one qualified bean disrupts the ability to inject another qualified bean by subtype #33399

Open
@stevenschlansker

Description

@stevenschlansker

Affects: spring-framework 6.1.12

We are trying to manage a fleet of similarly-typed beans to inject. We've found a surprising circumstance where the existence of a dependency on one bean ("A") will cause another bean ("B") to vary how you may inject it.

Below please find a test case showing the problem. The test case is attempting to inject @Named("B") IImpl<String> bStr, a concrete subtype of an interface I<String>. The subtype is desired to access test-stubbing features not present on the interface.

A different component is declaring @Named("A") I<UUID> aUuid. These definitions seem unrelated, since both the name (A vs B) and type (I<String> vs I<UUID>) differ.

The test fails as is:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'BeanInjectTest$IImpl<java.lang.String>' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@jakarta.inject.Inject(), @jakarta.inject.Named("B")}
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1880)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1406)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:785)
...
  • Changing injection of @Named("B") IImpl<String> to @Named("B") I<String> works, but then you cannot access the subtype's features
  • Commenting out the @Import(MyConfig.ConsumeA.class) causes the injection of B to work, despite only changing the use of A not B!

This behavior seems very surprising to me, and feels like a bug.
Thank you for your thoughts.

@SpringJUnitConfig({
    BeanInjectTest.MyConfig.class,
})
public class BeanInjectTest {
    @Inject
    @Named("B")
    IImpl<String> bStr;

    @Test
    public void injectB() {
        assertThat(bStr).isInstanceOf(IImpl.class);
    }

    @Import({
        MyConfig.ConsumeA.class,
    })
    public static class MyConfig {
        @Bean
        @Named("B")
        public I<String> bStr() {
            return new IImpl<>();
        }

        @Named("A")
        @Bean
        public I<UUID> aUuid() {
            return new IImpl<>();
        }

        public static class ConsumeA {
            public ConsumeA(
                    @Named("A")
                    final I<UUID> aUuid) {
                System.err.println("aUuid: " + aUuid);
            }
        }
    }

    public interface I<T> {}
    public static class IImpl<T> implements I<T> {}
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    in: coreIssues in core modules (aop, beans, core, context, expression)status: waiting-for-triageAn issue we've not yet triaged or decided on

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions