河南艾特网站建设公司网站免费进入窗口软件有哪些
一、 前言
平常我们在使用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 {@Autowiredprivate TestParent testParent;@PostConstructpublicvoidinit() {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 {@Autowiredprivate TestParent test;@PostConstructpublic 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调试分析。
