Spring 循环依赖
在下文中我将对循环依赖的过程和实现做详细的描述, 以 Spring 的源码过程为主导逐步分析。 里面包含一些属性赋值的前置知识,可以在前面几篇文章中获取相关的信息。
Spring 解决了那些循环依赖场景
Spring 只支持单例非懒加载场景的循环依赖,不能解决构造注入,以及懒加载的循环依赖问题。
Spring 循环依赖定义
顾名思义,循环依赖就是说有 A, B 两个类在 A 中引用 B,并且在 B 中引用 A。这个就是一个简单的循环依赖。
例如: 在A类中通过构造方法注入B,在B类中通过构造方法注入。这种方式互相注入 Spring IOC 容器是无法完成的,回抛出 BeanCurrentlyInCreationException
。
依赖注入过程中, Bean A 与 Bean B 之间的循环依赖关系,需要其中一个 Bean 在未完成初始化之前被另外一个 Bean 注入。这就是一个类似先有鸡和先有蛋的问题。
一个简单的例子:
// A 依赖 B @Component public class A { @Autowired private B b; } // B 依赖 A @Component public class B { @Autowired private A a; }
Spring 循环依赖处理过程
下面是我自己花的一个草图,前置条件是: A 类,B类互为循环依赖,当 a 先加载 b 后加载,这个时候应该是从 doCreateBean
开始逐步查找。
以上面 A 类 和 B 类的循环依赖过程来说
- 首先入口方法
AbstractAutowireCapableBeanFactory#doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
以此为入口经过了一下的几个流程处理
//1. 创建 Bean 实例可以获取到一个 BeanWrapper 对象 instanceWrapper = createBeanInstance(beanName, mbd, args); //2. 合并BeanDefinitionPostProcessor applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); //3. 属性填充 populateBean(beanName, mbd, instanceWrapper); //4. Bean 初始化 exposedObject = initializeBean(beanName, exposedObject, mbd); //5. 注册 Bean 的销毁方法 registerDisposableBeanIfNecessary(beanName, bean, mbd);
- 循环依赖的几个步骤, (1). 在属性填充
populateBean
方法调用之前,将instanceWrapper
放入三级缓存singletonFactories
中,调用代码:addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
注意,这里三级缓存中存放的是ObjectFactory
对象工厂,目的是为了解决该对象可能被代理,如果我们直接缓存该对象那么就会存在Bean对象a
的原始对象和代理对象最终不一致的情况,具体的 AOP 代理部分我会在后续的文章中提到。首先 ()->{} 用法是一个标准的 拉姆达表达式。我们再来看看addSingletonFactory
方法是如何实现的。
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { //三级缓存,缓存的是ObjectFactory,表示对象工厂,用来创建某个对象的。 this.singletonFactories.put(beanName, singletonFactory); //二级缓存,缓存的是早期的bean对象。表示Bean的生命周期还没走完就把这个Bean放入了earlySingletonObjects。 this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } }
(2). 后续会经历,A类 去查找依赖 B类,然后通过 getBean
方法创建 B 类, 然后 B 类又依赖 A 类那么再会通过 getBean
方法来查找 a 类。经过这些流程我们就来到了 。AbstractBeanFactory#doGetBean
方法,然后再通过 getSingleton
来获取Bean, 我们先来看看这个方法的实现是怎么样的:
protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 单例池中获取是否存在 Object singletonObject = this.singletonObjects.get(beanName); // 正在创建 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // 二级缓存 earlySingletonObjects 存放的有可能是经过AOP增强的代理对像 singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { // 三级缓存 singletonFactories 用于 Bean 的早期暴露以解决循环依赖 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); // 放到二级缓存中 this.earlySingletonObjects.put(beanName, singletonObject); // 从三级缓存中移除 this.singletonFactories.remove(beanName); } } } } return singletonObject; }
由于我们 A 类已经在创建中,所以我们拿到的应该是一个 A 类的 ObjectFactory 此时,Bean a 就由3级缓存 singletonFactories
进入二级缓存 earlySingletonObjects
, 如果此时多次访问,那么我们可以直接在二级缓存 earlySingletonObjects
中 get
。
(3). 那么此时 Bean b
也同样要走这样的流程,最终 b
完成创建之后会来到 addSingleton(beanName, singletonObject);
因为先创建的 a
并且 a
依赖 b
所以在完成 a
创建之前需要先完成 b
的创建。我们再说说 addSingleton
方法, 它主要就是将 bean 从二,三级缓存中移除,然后放入一级缓存 singletonObjects
中。
protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { this.singletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }
Spring 循环依赖总结
- 三级缓存
registeredSingletons
对于我看来作用主要是用来:作为一个早期对象的暴露,他可能是一个代理工厂,也可能是一个原始对象,这里具体取决于上下文场景。
- 二级缓存
earlySingletonObjects
作为一个不完整的对象来暴露,此时对象没有完成属性的赋值,但是它里面的的对象可以作为自动注入的对象,注入给使用者。
- 一级缓存
singletonObjects
存放的一个完整的对象,标志单实例 Bean 已经完成创建。