一、 前言
平常我们在使用spring框架开发项目过程中,会使用@Autowired
注解进行属性依赖注入,一般我们都是声明接口类型来接收接口实现变量,那么使用父类类型接收子类变量,可以注入成功吗?答案是肯定可以的!
二、结果验证
我们在项目中声明如下三个类:
1. 测试代码
- TestParent
public class TestParent {
protected void test() {
System.out.println("I am TestParent...");
}
}
- TestSon
importorg.springframework.stereotype.Component;
@Component
public class TestSon extends TestParent {
publicvoidtest() {
System.out.println("I am TestSon...");
}
}
- TestType
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Component;
importjavax.annotation.PostConstruct;
@Component
public class TestType {
@Autowired
private TestParent testParent;
@PostConstruct
publicvoidinit() {
System.out.println("=========================");
testParent.test();
System.out.println("=========================");
}
}
2. 验证测试
启动项目:
可以看到注入成功了,说明依赖注入使用父类类型接收子类变量是没有问题的。
3. @Autowired注解使用细节
还是上面的案例,我们修改一下TestParent类的代码,把TestParent也交由Spring容器管理:
importorg.springframework.stereotype.Component;
@Component
public class TestParent {
protected void test() {
System.out.println("I am TestParent...");
}
}
运行测试:
可以发现,此时也可以注入成功,但是执行对象变成了父类,有经验的大佬已经猜到是什么情况了,没猜到的也没有关系,我们再修改一下TestType类的代码:
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Component;
importjavax.annotation.PostConstruct;
@Component
public class TestType {
@Autowired
private TestParent test;
@PostConstruct
public void init() {
System.out.println("=========================");
test.test();
System.out.println("=========================");
}
运行结果:
此时居然注入报错了,提示我们有两个bean冲突了,不能进行依赖注入!
三、原理分析
为什么会出现上面的现象呢,是由于@Autowired
注入时,是先按照类型找到bean
实例名称,再按照beanName
去获取真正需要注入的bean
,如果有多个实例时,会尝试通过需要注入的字段名称与按照类型筛选出来的beanName
对比,如果能够对比出唯一beanName
,也会按照此beanName
去获取bean
实例注入,如果不能够确定唯一bean
实例,就会抛出异常了。
下面我们进行源码跟踪:
@Autowired
注解解析的核心逻辑入口在org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
后置处理器中,我们进入该类中进行断点调试。
通过阅读源码我们可以发现,依赖注入入口方法是postProcessProperties()
方法:
进入org.springframework.beans.factory.annotation.InjectionMetadata#inject
方法:
继续调试,由于我们目前是字段方式注入,所以选择org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement
类:
查看方法细节:
我们继续跟踪org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue
方法:
进入org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency
方法:
继续进入org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency
方法:
继续进入org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates
方法:
进入org.springframework.beans.factory.BeanFactoryUtils#beanNamesForTypeIncludingAncestors(org.springframework.beans.factory.ListableBeanFactory, java.lang.Class, boolean, boolean)
看一下bean
的筛选逻辑:
跟踪进入org.springframework.beans.factory.support.DefaultListableBeanFactory#doGetBeanNamesForType
方法:
查看类型匹配判断方法org.springframework.beans.factory.support.AbstractBeanFactory#isTypeMatch(java.lang.String, org.springframework.core.ResolvableType, boolean)
:
判断核心方法org.springframework.core.ResolvableType#isInstance
:
看到isAssignableFrom
方法就知道为什么子类变量也可以成功注入父类类型了,此时子类变量也是可以成功匹配上的。
筛选出所有类型匹配的beanName
以后,回到org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates
做一下是否可以进行依赖注入的判断,返回beanName
信息:
然后回到org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency
方法中,如果有多个类型,会按照字段名称和beanName
匹配再筛选:
最终确定获取到可以注入的bean
实例。
四、写在最后
以上流程还是比较清晰的,分析过程中有一些分支流程没有过度关注,有兴趣的小伙伴也可以参考流程,自己进行debug调试分析。