I'm studying the source code of Spring today,and I have a problem that can not understand: there is a length of source code in resolveDependency->doResolveDependency->findAutowireCandidates:

Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
    Class<?> autowiringType = classObjectEntry.getKey();
    if (autowiringType.isAssignableFrom(requiredType)) {
        Object autowiringValue = classObjectEntry.getValue();
        autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
        if (requiredType.isInstance(autowiringValue)) {
            result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
            break;
        }
    }
}

What is the meaning of if (autowiringType.isAssignableFrom(requiredType))?

I do some experiments for it:

First, create classes A, A1, A2. A1 extends A. A2 extends A1. There is no code in A, A1, A2.

Second, create User, and there is only one field in User:

public class User {

    @Autowired
    private A1 a1;

    public A1 getA1() {
        return a1;
    }

    public void setA1(A1 a1) {
        this.a1 = a1;
    }
}

findAutowireCandidates.xml:

    <bean class="A1" name="a1"/>
    <bean class="A2" name="a2"/>
    <bean class="findAutowireCandidates.User" name="user" autowire="byType"/>
    <context:component-scan base-package="findAutowireCandidates"/>

In this case, Spring will throw an exception, because too many beans match a1 in User. We can use @Primary or others to avoid this exception, but this is not what we talk about today. By another solution, we create a custom BeanFactoryPostProcessor, and register a new A2 into resolvableDependencies:

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
      configurableListableBeanFactory.registerResolvableDependency(A2.class,new A2());
  }
}

At last:

public class TestFindAutowireCandidates {
  public static void main(String[] args) {
      ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("findAutowireCandidates.xml");
  }
}

Run this demo, it will end with the same exception anyway. And then, I change MyBeanFactoryPostProcessor:

configurableListableBeanFactory.registerResolvableDependency(A.class,new A2());

Run again, it will not throw exception any more.

All these because of if (autowiringType.isAssignableFrom(requiredType)) that I mentioned before, if autowiringType is superclass of requiredType or it’s requiredType itself, isAssignableFrom will return true.

The purpose of findAutowireCandidates probably is finding out all of candidates that match requiredType. First look up matches in resolvableDependencies, and then look up from all of beans in factory. I have no problem with the latter, mainly the former.

Why is autowiringType filtered out if it is a subclass of requiredType when searching in resolvableDependencies? This is the first case of error reporting in the previous experiment: if I register the key value pair A2.class and new A2() in resolvableDependencies, A2 will be filtered out here because A2 is a subclass of A1, resulting in the MyBeanFactoryPostProcessor being written as if it had not been written; But if you register A.class, new A2() will take effect.

But why does Spring do that?

If you don't have this if (autowiringType.isAssignableFrom(requiredType)), isn't it OK?

Is if (requiredType.isInstance(autowiringValue)) not sufficient on its own?

Comment From: qclucky7

I was in the spring springboot-version: 2.2.2 RELEASE spring-version: 5.2.2.RELEASE It works。

@Component
public class A {

    private String name = "A";
}
@Component
public class A1 extends A{

    private String name = "A1";
}
@Component
public class A2 extends A1{

    private String name = "A2";
}
@Component
public class AutowireCandidatesTest implements InitializingBean {

    @Autowired
    private A1 a1;

    @Override
    public void afterPropertiesSet() throws Exception {
        Field[] fields = ReflectUtil.getFields(a1.getClass());
        System.out.println(Arrays.toString(fields));
    }
}

Comment From: snicoll

@JavaFenSi sorry it took so long to look at this. Unfortunately, we don't use the issue tracker for questions and, in absence of a concrete problem, this issue is not actionable. I can't say from the top of my head why this check exists.