1.spring的循环依赖简介

什么是spring的循环依赖?

spring容器的bean互相依赖形成闭环,称为spring的循环依赖。

spring循环依赖只能用于set方法和成员变量注入实现,且要求是单例bean,多例(原型bean)是不能实现循环依赖的。

构造方法注入是不能实现循环依赖的,会抛出异常(Requested bean is currently in creation: Is there an unresolvable circular reference?)

@service
class UserServiceImpl implments UserService{
     @Autowried
    private EmailService emailService;
}
@service
class EmailServiceImpl implments EmailService{
     @Autowried
    private UserService userService;
}

为什么Spring解决循环依赖比较麻烦呢?因为Spring创建一个Bean是需要通过反射来构建的,构建过程中无法感知这个类具体是什么类型的,它只能够实例化一个填充一个实体!于是:

创建 UserServiceImpl完成后发现依赖EmailServiceImpl !

于是创建EmailServiceImpl ,但是创建完成后又发现依赖于UserServiceImpl!

于是又去创建UserServiceImpl,又发现EmailServiceImpl !

然后我又去创建EmailServiceImpl

2.如何解决循环依赖?

实例化:调用构造函数将对象创建出来

初始化:调用构造函数将对象创建出来后,给对象的属性也被赋值

可以创建两个容器(Map),一个起名为singletonObjects,一个起名为earlySingletonObjects!

singletonObjects」:单例池,我们去存放已经创建完成,并且属性也注入完毕的对象!

earlySingletonObjects」:提前暴露的对象,存放已经创建完成,但是没有注入好的对象!

我们有了这两个Map对象,再次试图创建一个被循环依赖的bean!

创建 UserServiceImpl完成后,把自己存到「earlySingletonObjects」里面去,然后发现依赖EmailServiceImpl !

于是试图从「singletonObjects」寻找,很显然是没有的,然后到「earlySingletonObjects」里面寻找发现也没有,开始新建!

创建EmailServiceImpl 完成后,把自己存放到「earlySingletonObjects」里面去,然后发现依赖UserServiceImpl!

于是试图从「singletonObjects」寻找,很显然是没有的,然后到「earlySingletonObjects」里面寻找,发现了UserServiceImpl对象!

将「earlySingletonObjects」返回的对象UserServiceImpl设置到EmailServiceImpl 中去,创建完成!

把自己放置到「singletonObjects」里面,然后把自己从「earlySingletonObjects」删除掉!返回!

UserServiceImpl将返回的EmailServiceImpl 设置到对应的属性中,创建完成!

把自己放置到「singletonObjects」里面,然后把自己从「earlySingletonObjects」删除掉!返回!

3.Spring为什么使用三级缓存解决呢?

通过上面的解释我们大概明白了循环依赖的解决方案,明明采用二级缓存就能够解决循环依赖,但是Spring为什么使用了三级缓存呢?

我们先来了解一下Spring每个缓存的名字及其作用:

「singletonObjects」:单例池,我们去存放已经创建完成,并且属性也注入完毕的对象!

「earlySingletonObjects」:提前暴露的对象,存放已经创建完成,但是没有注入好的对象!

「singletonFactories」:提前暴露的对象,存放已经创建完成,但是还没有注入好的对象的工厂对象!通过这个工厂可以返回这个对象!

为什么明明使用二级缓存就能够解决的问题,spring偏偏要使用三级缓存去解决呢?

上面的设计方案二级缓存是能够很好的解决循环依赖所带来的问题,但是请大家思考一个问题:

我们创建的bean所依赖的对象是一个需要被Aop代理的对象,怎么办?遇到这种情况,我们肯定不能够直接把创建完成的对象放到缓存中去的!为什么,因为我们期望的注入的是一个被代理后的对象,而不是一个原始对象! 所以这里并不能够直接将一个原始对象放置到缓存中,我们可以直接进行判断,如果需要Aop的话进行代理之后放入缓存!

但是Aop的操作是在哪里做的?是在Spring声明周期的最后一步来做的!如果我们进行判断创建的话,Aop的代理逻辑就会在创建实例的时候就进行Aop的代理了,这明显是不符合Spring对于Bean生命周期的定义的! 所以,Spring重新定义了一个缓存【「singletonFactories」】用来存放一个Bean的工厂对象,创建的对象之后,填充属性之前会把创建好的对象放置到【「singletonFactories」】缓存中去,并不进行实例化,只有在发生了循环引用,或者有对象依赖他的时候,才会调用工厂方法返回一个代理对象,从而保证了Spring对于Bean生命周期的定义!

以下是spring关于三级缓存的定义:

主要原理是利用三级缓存机制:

Map<String, Object> singletonObjects: 一级缓存,也就是我们平常理解的单例池,存放已经完整经历了完整生命周期的bean对象。

Map<String, Object> earlySingletonObjects: 二级缓存,存储早期暴露出来的bean对象,bean的生命周期未结束。(属性还未填充完)

Map<String,ObjectFactory<?> > singletonFactories: 三级缓存,存储生成bean的工厂。

注意:只有单例bean会通过三级缓存提前暴露出来解决循环依赖的问题,而非单例的bean,每次从容器中获取都是一个新的对象,都会重新创建,所以非单例bean是没有缓存的,不会将期放到三级缓存中。

4.spring三级缓存的大致流程

A在创建过程中需要B,于是A先将自己放到三级缓存里面,去实例化B

B实例化的时候发现需要A,于是B先查一级缓存,没有再查二级缓存,还是没有,再查三级缓存,找到了A;然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A

B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态);然后回来接着创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将A放入到一级缓存中

在这里插入图片描述

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐