Spring中的循环依赖问题

43 阅读3分钟

Spring 解决循环依赖

Spring 解决循环依赖全流程 经过上面的铺垫,我想你对 Spring 如何解决循环依赖应该已经有点感觉了,接下来我们就来看看它到底是如何实现的。 明确了 Spring 创建 Bean 的三步骤之后,我们再来看看它为单例搞的三个 map: 1.一级缓存,singletonObjects,存储所有已创建完毕的单例 Bean(完整的 Bean) 2.二级缓存,earlySingletonObjects,存储所有仅完成实例化,但还未进行属性注入和初始化的 Bean3.三级缓存,singletonFactories,存储能建立这个 Bean 的一个工厂,通过工厂能获取这个 Bean,延迟化 Bean 的生成,工厂生成的 Bean 会塞入二级缓存 这三个 map 是如何配合的呢? 1.首先,获取单例 Bean 的时候会通过 BeanName 先去 singletonObjects(一级缓存)查找完整的 Bean,如果找到则直接返回,否则进行步骤 2。 2.看对应的 Bean 是否在创建中,如果不在直接返回找不到(返回null),如果是,则会去 earySingletonObjects(二级缓存)查找 Bean,如果找到则返回,否则进行步骤 3 3.去 singletonfactories(三级缓存)通过 BeanName 查找到对应的工厂,如果存着工厂则通过工厂创建 Bean,并且放置到 earlySingletonObiects 中。 4.如果三个缓存都没找到,则返回 null 从上面的步骤我们可以得知,如果查询发现 Bean 还未创建,到第二步就直接返回 nul,不会继续查二级和三级缓存返回 nul 之后,说明这个 Bean 还未创建,这个时候会标记这个 Bean,正在创建中,然后再调用 createBean 来创建 Bean,而实际创建是调用方法 doCreateBean。 doCreateBean 这个方法就会执行上面我们说的三步骤: 1.实例化 2.属性注入 3.初始化

在实例化 Bean 之后,会往 singletonFactories 塞入一个工厂,而调用这个工厂的 getObject 方法,就能得到这个Bean. java 复制代码

addSingletonFactory(beanName,()->getEarlyBeanReference(beanName, mbd, bean));

要注意,此时 Spring 是不知道会不会有循环依赖发生的,但是它不管,反正往 singletonfactories 塞这个工厂,这里就是提前暴露。 然后就开始执行属性注入,这个时候 A发现需要注入 B,所以去 aetBean(B),此时又会走一遍上面描述的逻辑,到了B的属性注入这一步。 此时 B调用 getBean(A),这时候一级缓存里面找不到,但是发现 A正在创建中的,于是去二级缓存找,发现没找到,于是去三级缓存找,然后找到了。 并且通过上面提前在三级缓存里暴露的工厂得到 A,然后将这个工厂从三级缓存里删除,并将 A加入到二级缓存中。 然后结果就是 B 属性注入成功。 紧接着 B 调用 initializeBean 初始化,最终返回,此时 B已经被加到了一级缓存里这时候就回到了 A的属性注入,此时注入了 B,接着执行初始化,最后 A 也会被加到一级缓存里,且从二级缓存中删除 A。Spring 解决依赖循环就是按照上面所述的逻辑来实现的。重点就是在对象实例化之后,都会在三级缓存里加入一个工厂,提前对外暴露还未完整的 Bean,这样如果被循环依赖了,对方就可以利用这个工厂得到一个不完整的 Bean,破坏了循环的条件。