注入现象
当我们在属性上面加上 @Autowired的时候,spring就要根据Type来注入实例了,那么到底会找哪个实例的如果有多个怎么办? 今天就来实验一下
多接口注入
当注入的属性接口下有多个实现。这个时候运行的话是
public class ModelTest {
@Test
public void defaultModel(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("com.spring.inject");
context.refresh();
I i = context.getBean(F.class).getI();
System.out.println("i==="+i);
}
}
@Component
public class F {
@Autowired
I i;
public I getI() {
return i;
}
}
public interface I {
}
@Component("a")
public class Ia implements I{
}
@Component("b")
public class Ib implements I{
}
报错如下:找到了多个实现接口类,不知道用哪个。
多接口制定名称注入
如果把F类中的属性名称指定。
@Component
public class F {
@Autowired
I a;
public I getI() {
return a;
}
}
成功运行,注入了指定名称的Ia实例。
现象总结
Autowired是通过Type来寻找类的,如果找到多个实现,则再按照名称来寻找指定的类,最终确定一个类,注入到属性中。
下面我们来通过源码验证一下这个结论。
源码
点击Autowired进入该类,发现没有什么东西;但是类头部的注解让我们跳转这个类AutowiredAnnotationBeanPostProcessor,这明显就是Autowired的处理器。
这个类中有两个主要方法
AutowiredFieldElement和AutowiredMethodElement,意思是Autowired注解是在属性上和方法上。我们要看得肯定是第一个AutowiredMethodElement.inject()这个方法
红框的上半部分是缓存,如果解析过一次的话会走上面。我们主要看红框下半部分resolveMethodArguments方法。看这个方法名字就很清晰了,解析属性值,解析什么属性值呢,肯定是加了Autowired注解的属性。
DependencyDescriptor是一个依赖描述器,比如刚才例子中,会说明是在F类中的I属性上有这个Autowired注释,属性的名字是a等等数据。
然后通过这个依赖描述器,到beanFactory.resolveDependency()这个方法中进行处理。
这个方法中也有好多内容,现在可以先不用管,后面会慢慢分析那些懒加载之类的东西。只需要看doResolveDependency()这个方法。
这个方法中的这一行比较重要,是获取到多个属性的多个Bean之后直接注入到属性当中,不再按照名称筛选出单个。这也就是为什么当我们在List或者Map属性上加Autowired注释的时候,可以直接将接口下的所有实现类注入的原理。核心就是这个方法resolveMultipleBeans()。
这个方法中也简单就是判断一下Autowired注释下的属性是否是Array或者Map等数组集合类型,如果是的话,就将查询到的多个Bean直接塞进去返回,如果不是的话就返回一个空。
这里一旦返回一个空,就证明这个Autowired注释下的属性只能注入一个Bean。
顺着刚才的代码继续往下看,findAutowireCandidates()这个方法就是按照注释下的属性查找Bean。通过例子来解释的话,就是按照“I”这个类,查询到下面的两个实现类“Ia”和“Ib”。如果没有查询到的话就返回一个null。
下面还有一个判断如果查询到多个的话,就要进行筛选了。主要是通过这个方法determineAutowireCandidate()
可以看到,这个方法里面有个for循环,很简单嘛,意思就是通过属性的名字筛选多个Bean,如果名称一样的话,就返回对应的Bean。只要遍历到一个相同的就直接返回,因为在Spring中不可能有多个Bean的名字相同。
源码总结
- 当属性上有Autowired注解的时候
- 会先判断属性是否可以注入多个Bean,如果可以的话,直接将找到的多个注入进集合中
- 如果不可以的话,判断找到一个还是多个
- 找到一个Bean则直接返回
- 找到数量大于1个的话,将多个Bean遍历,与属性名称进行对比,一致则直接返回。