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.