自动注入原理
假定 Spring 容器按下图组织管理 Bean 对象

@Autowired:默认按类型注入,当容器中存在多个相同类型的Bean时,按变量名查找。按类型注入理解成不需要指定名字。
使用 @Autowired 自动注入 Bean 对象时的流程猜测如下:
先使用 byType 方式获取一列数据(map)
如果
map.size() == 1,则获取这唯一一个元素如果
map.size() > 1,那么通过变量名去 map 中获取 Bean 对象,即map.get()如果
map.get()返回值为 null,那么会报错。这种错误未必会被 IDEA 检测出来,因为有些 Bean 是由
factoryBean.getObject()生成的,可能手动注入一个 Bean,又通过 FactoryBean 注入,此时 IDEA 检测不出来,但是在运行时会报错。因此,使用@Autowired在编写时没有提示报错不代表自动注入没有 bug。
显式声明高于可推断的优先级,因此@Primary注解的优先级高于变量名推断。
@Qualify注解和@Autowired搭配使用,@Primary注解搭配@Bean注解使用;前者用于获取Bean,后者用于注册Bean。
@Autowired 自动注入的三种方式
字段注入
- 通过反射直接为属性赋值,不会执行setter方法
- 以递归地方式进行依赖注入,会有循环依赖的问题(
a->b->a->b->...)
不能为 final 修饰的字段进行注入,因为 final 修饰的字段必须在实例化的时候就进行赋值。
Setter 方法注入
当存在循环依赖 a->b->a 时,setter 方式解决循环依赖:
- Spring 先创建 a 的默认实例(基本类型的值为0或false,对象类型的值为null),将其放入到缓存中
- 然后,正常创建依赖项 b 的实例。b 实例会从缓存中获取 a 实例进行 setter 注入
- 最后,完成 a 实例的 setter 注入
构造器注入
- 可以注入 final 修饰的字段
- 存在循环依赖的问题
可以为 final 修饰的字段进行赋值。
当@Component标注的Spring组件类仅有一个构造器时,可以省略@Autowired。即从容器中查找参数并注入是作为框架的默认行为,本质上就是框架按照它自己的规则向容器中注入Bean,而Bean的生成规则就是框架自定义的一套流程。
构造器注入和setter注入本质上和带形参的@Bean方法相同。
Spring中的循环依赖为什么用三层缓存
针对循环依赖这个问题,实际上只需要两层缓存即可解决。
而 Spring 中解决循环依赖使用了三层缓存是因为 Spring 还考虑了动态代理(AOP)生成的代理对象,再考虑动态代理生成的代理类对象的基础上,两层缓存就无法解决问题了。