Loading

Spring源码学习笔记9——构造器注入及其循环依赖

系列文章目录和关于我

一丶前言

前面我们分析了spring基于字段的和基于set方法注入的原理,但是没有分析第二常用的注入方式(构造器注入)(第一常用字段注入),并且在循环依赖问题上构造器注入常被说spring无法解决构造器注入的循环依赖,下面我们来分析构造器注入和其循环依赖的源码

二丶构造器依赖注入

在spring初始化每一个非抽象,单例,非懒加载的bean的时候,会调用createBeanInstance方法去创建bean的实例,在使用默认的策略——无参构造or CGLIB生成子类对象的方式之前,先会使用所有的SmartInstantiationAwareBeanPostProcessor 来判断是否有用户指定的后置处理器

1.循环调用所有的SmartInstantiationAwareBeanPostProcessor实现类来推断该使用哪个构造器

这里重点是使用到了AutowiredAnnotationBeanPostProcessor来推断

2.AutowiredAnnotationBeanPostProcessor 推断构造器的逻辑

首先AutowiredAnnotationBeanPostProcessor 有一个缓存key是bean类型,value是之前推断的所有构造器数组。自然是一波双if+synchronized方式来读缓存中内容,缓存没有命中再进行推断

遍历每一个构造器(这里省去了spring对Kotlin语言支持的部分)
1.获取当前构造器上面的@Autowired 和  @Value 注解
2.如果注解信息为空,且当类是一个被CGLIB代理后的类,
会获取父类构造器上面的注解(要求父类构造器和当前遍历到的构造器参数类型顺序相同)
3.如果注解信息不为空,会检查 required 属性的信息
(spring还支持ejb等的注解,所有这里检查require的信息)像@Autowired 和  @Value这种不包含required属性的注解会默认是required为true,
并且记录当前构造器为候选者,也就是说有注解的才算在候选者
4.如果存在多个required=true的构造器抛出异常,
否则使用遍历记录当前required=true的构造器
5.如果候选者不为空,required=true的构造器为空
(一般这种情况是标注了EJB的注解指定required为false)spring会把无参构造加入到候选者中
6.如果原来类只定义了一个有参构造,那么使用这个有参构造

如果不考虑EJB中的依赖注入注解
那么就是如果构造器有@Autowired or @Value那么默认使用它
如果定义了一个有参构造那么使用这个有参构造

其他情况一律返回null(后续spring可能使用无参构造or CGLIB生成子类的方式)

3.使用构造器进行依赖注入的逻辑

推断出使用哪个构造器之后,spring调用autowireConstructor方法进行构造器注入,具体逻辑委托给ConstructorResolver的autowireConstructor方法,最终解析依赖注入参数的在createArgumentArray方法中进行,一般是调用resolveAutowiredArgument方法

最终还是殊途同归的调用到了beanFactory的resolveDependency方法

4.实例化对象

这一步还是调用的instantiate方法

三丶为什么构造器的循环依赖spring无法解决

1.构造方法注入无法解决循环依赖的情况及其原因

如果是这种循环依赖的情况spring是无法解决循环依赖的,下面是这种循环依赖出现时候的代码流程

问题出现在beforeSingletonCreation方法中

在spring创建bean的之前使用了set维护正在创建bean的名称,B需要A,再次创建A的时候就发现set中原本存在A,这时候就是无法解决循环依赖的情况,会抛出异常

同理这种情况也是不可以解决的

2.构造方法注入可以解决循环依赖的情况及其原因

这种情况下spring又是可以解决循环依赖的

原因是A是可以成功使用无参构造实例化的,所以B需要A的时候可以在三级缓存拿到A的ObjectFactory,调用后得到A,而不是再次创建A

我们可以得到结论

如果加载顺序是A然后B,那么A只能是字段注入or方法注入,不能是构造器注入
B无所谓那种注入方式
posted @ 2022-08-01 07:12  Cuzzz  阅读(1212)  评论(0编辑  收藏  举报