自动注入原理

假定 Spring 容器按下图组织管理 Bean 对象

Spring容器管理Bean

@Autowired:默认按类型注入,当容器中存在多个相同类型的Bean时,按变量名查找。按类型注入理解成不需要指定名字。

使用 @Autowired 自动注入 Bean 对象时的流程猜测如下:

  1. 先使用 byType 方式获取一列数据(map)

  2. 如果 map.size() == 1,则获取这唯一一个元素

  3. 如果 map.size() > 1,那么通过变量名去 map 中获取 Bean 对象,即 map.get()

  4. 如果 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 方式解决循环依赖:

  1. Spring 先创建 a 的默认实例(基本类型的值为0或false,对象类型的值为null),将其放入到缓存中
  2. 然后,正常创建依赖项 b 的实例。b 实例会从缓存中获取 a 实例进行 setter 注入
  3. 最后,完成 a 实例的 setter 注入

构造器注入

  • 可以注入 final 修饰的字段
  • 存在循环依赖的问题

可以为 final 修饰的字段进行赋值。

当@Component标注的Spring组件类仅有一个构造器时,可以省略@Autowired。即从容器中查找参数并注入是作为框架的默认行为,本质上就是框架按照它自己的规则向容器中注入Bean,而Bean的生成规则就是框架自定义的一套流程。

构造器注入和setter注入本质上和带形参的@Bean方法相同。

Spring中的循环依赖为什么用三层缓存

针对循环依赖这个问题,实际上只需要两层缓存即可解决。

而 Spring 中解决循环依赖使用了三层缓存是因为 Spring 还考虑了动态代理(AOP)生成的代理对象,再考虑动态代理生成的代理类对象的基础上,两层缓存就无法解决问题了。


   转载规则


《》 熊水斌 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
IDEA导入本地jar包 数据库提高批量插入的效率 使用prepareStatement 攒sql 设置不允许自动提交数据 数据库事务 例如转账这件事,要么两个人的数据都改,要么都不改 回滚操作 需要保证数据不被提交 哪些操作会
2023-05-17
下一篇 
@Value 不仅仅局限于值注入,或者说一切的高级功能最后都落地于值注入。下面我们使用 @Value 来完成一些更高级的注入。 ${}:解析环境(Environment)中的值,本质上是通过 key 获取 value #{}:解析 Sp
2023-05-14
  目录