1.什么是循环依赖?当多个Bean相互依赖时则构成了循环依赖,例如A,B两个Bean 。其中A中存在属性B,B中存在属性A,当Spring在实例化A时发现A中存在属性B,就去实例化B,实例化B时又发现存在属性A,一直在循环注入依赖,导致循环依赖问题出现 。
2.Spring是怎么解决循环依赖的?Spring中会通过各种Bean中间状态来达到Bean还未实例化完成时提前将Bean提前注入到依赖Bean的属性中,假设说Bean有三种状态分别是青年态(一级缓存)、胚胎态(二级缓存)、小蝌蚪态(三级缓存)其中青年态代表Bean已经实例化完成,可以直接使用了,胚胎态代表Bean已经存在了但是还在创建中,还未创建完毕,小蝌蚪态代表还未开始创建,但是随时可以进行创建,三个状态就类似于三个等级,可以逐步提升从小蝌蚪状态提升到胚胎状态然后再提升到青年态,然后Spring开始创建Bena时会提前将Bean存放到小蝌蚪态的缓存集合中,当发现存在循环依赖时会使用存在于小蝌蚪状态缓存集合中的Bean,提前
3.循环依赖的案例假设例子中存在BeanA、BeanB、BeanC、BeanD四个Bean,其中
- BeanA依赖着BeanB,C
- BeanB依赖着BeanC
- BeanC依赖着BeanA
- BeanD依赖着BeanA,B,C
那么他们的实例化流程是什么样的呢?
文章插图
发现了吧,Spring解决循环依赖的法宝就是缓存 。
3.代码解析(只保留相关代码)1.检查缓存中是否已经存在实例化完毕的Bean
protected Object getSingleton(String beanName, boolean allowEarlyReference) {//首先检查一级缓存中是否存在Object singletonObject = this.singletonObjects.get(beanName);/***如果一级缓存中不存在代表当前 Bean 还未被创建或者正在创建中*检查当前 Bean 是否正处于正在创建的状态中(当Bean创建时会将Bean名称存放到 singletonsCurrentlyInCreation 集合中)*/if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {//检查二级缓存中是否存在singletonObject = this.earlySingletonObjects.get(beanName);/*** @@如果二级缓存中不存在 并且 允许使用早期依赖* allowEarlyReference : 它的含义是是否允许早期依赖* @@那么什么是早期依赖?* 就是当Bean还未成为成熟的Bean时就提前使用它,在实例化流程图中我们看到在添加缓存前刚刚实例化Bean但是还未依赖注入时的状态*/if (singletonObject == null && allowEarlyReference) {//获取三级缓存中的 Bean ObjectFactoryObjectFactory singletonFactory = this.singletonFactories.get(beanName);//如果 Bean 对应的 ObjectFactory 存在if (singletonFactory != null) {//使用 getObject 方法获取到 Bean 的实例singletonObject = singletonFactory.getObject();//将 bean 从三级缓存提升至二级缓存this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return singletonObject;}
2.创建Bean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {/*** 我们一开始通过 getSingleton() 方法中获取三级缓存中存放的Bean,这里就是向三级缓存中添加 bean 的地方* 流程:* 1.检查当前 bean 是否为单例模式,并且是否允许循环引用[讲解1],并且当前是否正在创建中(在getSingleton方法中添加的)* 2.如果允许提前曝光[讲解2],addSingletonFactory() 方法向缓存中添加当前 bean 的 ObjectFactory** [讲解1]:当前 Bean 如果不允许循环引用(循环依赖也就是被依赖),则这里就不会提前曝光,对应的 ObjectFactory* 则当发生循环依赖时会抛出 BeanCreationException 异常** [讲解2]:提前曝光的含义就是说当 bean 还未创建完毕时就先将创建中状态的bean放到指定缓存中,为循环依赖提供支持*/boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));//需要提前曝光if (earlySingletonExposure) {/*** 向缓存(三级缓存)中添加当前 bean 的 ObjectFactory*/addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}/*** Initialize the bean instance.* 初始化 Bean 实例阶段*/Object exposedObject = bean;try {/***依赖注入这时会递归调用getBean*/populateBean(beanName, mbd, instanceWrApper);//调用初始化方法,如:init-methodexposedObject = initializeBean(beanName, exposedObject, mbd);}/*** 当允许提前曝光时进入判断* @这里做了什么?* 1.检查当前bean是否经历了一场循环依赖*- 通过 getSingleton(beanName,false) 获取缓存中的 bean,传入 false 代表不获取三级缓存中的bean*- 为什么说 检查当前bean是否经历了一场循环依赖呢? 因为上述说了传入 false 代表不获取三级缓存的*- 那么什么情况下才会存在与一级缓存和二级缓存呢?答案就是循环依赖后 [解释1] 和bean实例化完成后*- 所以如果 getSingleton 返回的 bean 不为空,则这个bean就是刚刚经历了循环依赖** 2.检查提前曝光的bean和当前的Bean是否一致*- 下面有个判断 if (exposedObject == bean) ,这个判断从缓存中获取的bean 和 经历过初始化后的 bean*- 是否一致,可能我们有点晕,这里解释一下,缓存从的bean是什么时候存进去的?是在 addSingletonFactory 方法(649行)*- 然后这里存进去的 bean 只是提前曝光的 bean,还没有依赖注入和初始化,但是在依赖注入和初始化时都是可能直接改变*- 当前 bean 的实例的,这意味着什么?意味着经历了依赖注入和初始化的bean很可能和缓存中的bean就已经完全不是一个 bean了*下面讲解当一致或不一致时的逻辑:* 2.1 一致:*不是很理解,直接赋值,可是经历了各种 BeanPostProsser 或者依赖注入和初始化后不是就不一样了吗* 2.2 不一致:*看下方对于 else if 代码块的解释** @[解释1]* 当循环依赖时,A依赖着B,B依赖着A,实例化A首先将A放到三级缓存中然后发现依赖着B,然后去实例化B,发现依赖着A* 发现A在三级缓存,然后获取三级缓存中的bean并且将A从三级缓存中提升到二级缓存中,实例化B完成,接着实例化A也完成 。** @通俗讲解* 假设我们业务上对某种数据加了缓存,假设 i 在缓存中存的值为1,当我在数据库中把 i 的值改成 2 时,缓存中的 i 还没有被改变还是 1* 这时的数据已经和我们的真实数据偏离了,不一致了,这时有两种解决方式:1.服务器检查到数据不一致抛出异常 。(也就是进入else if 代码块)* 2.直接使用原始值也就是1(也就是将 allowRawInjectionDespiteWrapping 改成 true),当然这两种方式明显不是我们正常数据库的操作,只是* 为了说明当前的这个例子而已 。**/if (earlySingletonExposure) {//获取缓存中(除三级缓存) beanName 对应的 beanObject earlySingletonReference = getSingleton(beanName, false);//当经历了一场循环依赖后 earlySingletonReference 就不会为空if (earlySingletonReference != null) {//如果 exposedObject 没有在初始化方法中被改变,也就是没有被增强if (exposedObject == bean) {//直接赋值? 可是经历了各种 BeanPostProsser 或者依赖注入和初始化后不是就不一样了吗exposedObject = earlySingletonReference;}/**** 走到 else if时说明 当前 Bean 被 BeanPostProessor 增强了* 判断的条件为:* 1.如果允许使用被增强的* 2.检查是否存在依赖当前bean的bean** 如果存在依赖的bean已经被实例化完成的,如果存在则抛出异常* 为什么抛出异常呢?* 因为依赖当前bean 的bean 已经在内部注入了当前bean的旧版本,但是通过初始化方法后这个bean的版本已经变成新的了* 旧的哪个已经不适用了,所以抛出异常**/else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {String[] dependentBeans = getDependentBeans(beanName);Set actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);for (String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,"Bean with name '" + beanName + "' has been injected into other beans [" +StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +"] in its raw version as part of a circular reference, but has eventually been " +"wrapped. This means that said other beans do not use the final version of the " +"bean. This is often the result of over-eager type matching - consider using " +"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");}}}}try {/*** Register bean as disposable.* 注册 Bean 的销毁方法拓展*/registerDisposableBeanIfNecessary(beanName, bean, mbd);}catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);}return exposedObject;}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 下周黄金走势分析及操作建议?黄金下周走势预测
- SpringBoot整合RabbitMQ四种交换机类型详解
- 深入学习spring cloud gateway 限流熔断
- MySQL高级进阶:关于InnoDB存储结构,一文深入分析讲解
- SEO竞争对手分析,值得关注的5个数据指标
- 程序员标配Springboot!终于有人把SpringBoot讲的通俗易懂了
- 还搞不清Spring 与 Spring MVC 容器之间的关系?
- 基于Python获取股票分析,数据分析实战
- 购买茶叶哪个网站好,茶叶品类定义SWOT分析
- 普洱茶汤色品鉴分析,如何冲泡出诱人的熟普洱茶汤色