前面我们分析了spring基于字段的和基于set方法注入的原理,但是没有分析第二常用的注入方式(构造器注入)(第一常用字段注入),并且在循环依赖问题上构造器注入常被说spring无法解决构造器注入的循环依赖,下面我们来分析构造器注入和其循环依赖的源码
在spring初始化每一个非抽象,单例,非懒加载的bean的时候,会调用createBeanInstance方法去创建bean的实例,在使用默认的策略——无参构造or CGLIB生成子类对象的方式之前,先会使用所有的SmartInstantiationAwareBeanPostProcessor 来判断是否有用户指定的后置处理器
这里重点是使用到了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生成子类的方式)
推断出使用哪个构造器之后,spring调用autowireConstructor方法进行构造器注入,具体逻辑委托给ConstructorResolver的autowireConstructor方法,最终解析依赖注入参数的在createArgumentArray方法中进行,一般是调用resolveAutowiredArgument方法
最终还是殊途同归的调用到了beanFactory的resolveDependency方法
这一步还是调用的instantiate方法
如果是这种循环依赖的情况spring是无法解决循环依赖的,下面是这种循环依赖出现时候的代码流程
问题出现在beforeSingletonCreation方法中
在spring创建bean的之前使用了set维护正在创建bean的名称,B需要A,再次创建A的时候就发现set中原本存在A,这时候就是无法解决循环依赖的情况,会抛出异常
同理这种情况也是不可以解决的
这种情况下spring又是可以解决循环依赖的
原因是A是可以成功使用无参构造实例化的,所以B需要A的时候可以在三级缓存拿到A的ObjectFactory,调用后得到A,而不是再次创建A
我们可以得到结论 如果加载顺序是A然后B,那么A只能是字段注入or方法注入,不能是构造器注入 B无所谓那种注入方式