今天整理了一下Spring自动装配的过程,也突出了几个比较难以解答的问题.实践来求真知.
一. 自动装配过程
- 先按类型查找,若只有一个则直接返回
- 如果找到多个,则匹配名字
- 如果名字不一致,则报错.
二. 自动装配方式
构造器注入(推荐): 因为如果有一天脱离了Spring的环境,我们去使用这个自动注入的bean是,也可以通过他的构造函数来查看Bean之间的依赖关系
Setter注入:
三. 查看自动注入的几个特别现象
- 同时拥有同一类型的两个对象.
启动类测试
测试结果,报错,自动注入失败.
原因: 容器里有两个bean,回归自动装配过程,先按类型找找到两个bean,然后根据名称找发现person没有对应的对象,所以报错.
我们修改自动注入的名称为zhangsan试试.
测试结果,装配成功,对应着自动装配的过程,先按类型再按名称
- 相同类型存在两个bean,bean名称一致
测试结果为
这里注入了zhangsan那个bean,他是怎么确认是注入zhangsan这个bean的,在这种情形下是什么样的规则去注入呢?
我们尝试把person名称改成haha看是否有什么变化.
测试结果
发现还是注入了zhangsan那个bean.说明不是依靠这样的规则调整.
我们再尝试调换两个bean的声明顺序,看看是否会有变化
测试结果
发现自动注入的bean发生了改变,所以当出现两个bean类型相同,名称也相同时,spring优先注入先声明的bean.
但我们应该还知道一个primary注解,看看在这种情况会生效吗
测试结果
我们发现还是haha这个bean,说明Primary在此时是不起作用的.我们仔细深究一下Primary这个注解什么情况下才能使用.
我们把bean的name值去掉,尝试一下.
测试结果
说明Primary注解只在同类型存在多个bean时,指定一个bean为优先注入.而在同类型多个bean同名情况下不生效.
四. 几个特别的问题
1. 若多个bean名字相同时,如果注入到ioc容器,会产生什么情况
例如:
@Bean(name = "zhangsan")
public Person caca(){Person person = new Person("caca",2,"男");return person;
}
@Bean(name = "zhangsan")
public Person haha(){Person person = new Person("haha",2,"男");return person;
}
优先声明的注入的时候优先注入,也就是此时如果获取按名称获取zhangsan这个bean,将会提供person1这个对象.
获取对象
结果:
2. 假设有两个Bean,一个是单例的,一个是原型的,单例里面调了原型的,然后把这个单例注入到一个类,两次调单例的会是相同的对象嘛?相同的话,如何实现内部的原型的bean两次不同?
我们来实践看看会出现什么状态.
模拟一个单例,就用我们熟知的三层架构.controller层用来成为单例bean.
对于Service层的类,我们添加scope注解,将其变为原型类.
回到启动类,看看测试结果
发现了这两个bean是一样的,也就是说在单例bean的自动注入当中,哪怕你注入的是一个原型bean,他也仅仅只会注入一次.原型变成了单例.
这里是有一个常见的误解:单例Bean中的字段通常也是单例的,即使字段的类型是原型Bean。如果你将原型Bean注入到单例Bean的字段中,那么这个字段在单例Bean的生命周期内只会被初始化一次,也就是说,它实际上变成了单例的。
那回答后面那一个问题,我们怎么实现内部两次不一样呢?
我们现在采取的是通过单例自动注入的方式,原型bean被注入了一次.而我们仔细想想,原型bean的特征是使用到他自己本身才创建一个新的bean. 所以,我们可以通过动态构建的方式来完成.也就是我们通过容器去拿,这样就可以达到我们的目的.
修改controller层代码
我们再启动我们的启动类,结果为
3. Primary 和 Qualifier加在同一个bean上,哪个优先级更高
Qualifier的优先级更高.会优先使用Qualifier指定名称标注的bean.