Spring version: 5.2.8.RELEASE.

Code:

public class A {}

public class B extends A {}

public interface AService<T extends A> {}

public interface BService<T extends B> {}

public abstract class AbstractAService<T extends A> implements AService<T> {}

@Component
public class BServiceImpl<T extends B> extends AbstractAService<T> implements BService<T> {}

public class TempIT {

    @Autowired
    private AService<A> autoService;

    @Autowired
    private ApplicationContext context;

    @Test//PASSES
    public void test_autowired() {
        assertThat(autoService, is(notNullValue()));
    }

    @Test//FAILS
    public void test_manual() {
        AService<A> manualService = null;
        String[] beanNamesForType = context.getBeanNamesForType(ResolvableType.forClassWithGenerics(AService.class, A.class));
        if (beanNamesForType.length != 0) {
            manualService = (AService<A>) context.getBean(beanNamesForType[0]);
        }
        assertThat(manualService, is(notNullValue()));
    }
}

So, the problem is, that when AService<A> is auto wired everything works. However, when we try to get the same bean using ResolvableType.forClassWithGenerics we can't do it. I think this is a bug, but I am not a spring expert.

Comment From: encircled

Hi, Actually, it does not really work as you expect in neither case, because ? extends A != A. The reason why it seem to work when using Autowired, is because Spring does not check the generic types accurately during dependencies injection. To reproduce it, change your service component to

@Component
public class BServiceImpl<T extends B> extends AbstractAService<B> implements BService<B> {}

This would lead to AService<BeanB> to be injected into AService<BeanA> field, caused by presence of <T extends B> for some reason. Which is a different bug I think (@sbrannen please confirm, or am I missing smthing?)

As for forClassWithGenerics in general, it works correctly according to your generic types variance. You just need to specify the correct generic type bounds. Your BServiceImpl is ? extends B, but you are passing A. Tbh I don't know how to construct such type apart from getting it from the field like:

BServiceImpl<...> {
    public AService<? extends BeanA> service;
}
context.getBeanNamesForType(ResolvableType.forField(BServiceImpl.class.getDeclaredField("service")))

Comment From: snicoll

Actually, it does not really work as you expect in neither case, because ? extends A != A. The reason why it seem to work when using Autowired, is because Spring does not check the generic types accurately during dependencies injection.

Yes, this is correct, the unresolved generics handling will kick in eventually and inject the dependency in best effort style.