spring-projects / spring-projects/spring-framework

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

Open
#33,399 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

in: core status: waiting-for-triage
Dominant language
Java
Stars
60.2k
Forks
38.8k
Avg merge
5d 2h
Merged PRs (30d)
27

Description

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> {}
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the supplied BeanInjectTest and start tracing dependency resolution from DefaultListableBeanFactory.doResolveDependency and resolveDependency, along with AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement. The fix is complete when @Named("B") IImpl injects successfully while ConsumeA still injects @Named("A") I, and the test passes.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.