Spring解决循环依赖问题及三级缓存的作用

目录
  • 前言
  • 1什么是循环依赖
  • 2 如何解决循环依赖
  • 3无法解决的循环依赖

前言

所谓的三级缓存只是三个可以当作是全局变量的Map,Spring的源码中大量使用了这种先将数据放入容器中等使用结束再销毁的代码风格

Spring的初始化过程大致有四步:

  • 创建beanFactory,加载配置文件
  • 解析配置文件转化beanDefination,获取到bean的所有属性、依赖及初始化用到的各类处理器等
  • 刷新beanFactory容器,初始化所有单例bean
  • 注册所有的单例bean并返回可用的容器

我们说的循环依赖就是第四步在给Bean属性注入的时候发生的一个问题

1什么是循环依赖

循环依赖就是:

假设有两个类 A和B,A中需要注入B,B中需要注入A
由于A注入B时B没有创建,B创建时A也无法创建导致的死循环问题

2 如何解决循环依赖

我们都知道AOP是Spring的一个重要核心思想,其实现就是根据动态代理来实现的,也就是说我们的Bean其实很大概率都是要生成代理类,让我们先来看无代理的情况:

Bean的初始化大概是这样的:

根据以上步骤可以看出bean初始化是一个相当复杂的过程,假如初始化A bean时,发现A bean依赖B bean,即A初始化执行到了第4步填充属性,需要注入B bean,此时B还没有初始化,则需要暂停A,先去初始化B,那么此时new出来的A对象放哪里,直接放在容器Map里显然不合适,半残品怎么能用,所以需要提供一个可以标记创建中bean(A)的Map,可以提前暴露正在创建的bean供其他bean依赖,而如果初始化A所依赖的bean B时,发现B也需要注入一个A的依赖(即发生循环依赖),则B可以从创建中的beanMap中直接获取A对象(创建中)注入A,然后完成B的初始化,返回给正在注入属性的A,最终A也完成初始化,皆大欢喜。

如果没有循环依赖,A 依赖B,就是创建B,B依赖C就去创建C,创建完了逐级返回就行,并不需要什么缓存,所以,一级缓存之后的其他缓存(二三级缓存)就是为了解决循环依赖而设立的
一级缓存其实就是我们的成熟的Bean了,可以直接被使用

我们去看一下源码:

从源码中我们可以看到,三级缓存里放的并不是实例化的Bean,而是一个工厂,这是为什么呢?
循环依赖在实际应用可能会有,但很少,简单的应用场景是: controller注入service,service注入mapper,只有复杂的业务,可能service互相引用,有可能出现循环依赖,所以为了出现循环依赖才去解决,不出现就不解决,虽然支持循环依赖,但是只有在出现循环依赖时才真正暴露早期对象,否则只暴露个获取bean的方法,并没有真正暴露bean,因为这个方法不会被执行到,这块的实现就是三级缓存(singletonFactories),只缓存了一个单例bean工厂
为什么是一个工厂?或者说这个工厂的作用?
三级缓存bean工厂的getObject方式,实际执行的是getEarlyBeanReference,如果对象需要被代理(存在beanPostProcessors -> SmartInstantiationAwareBeanPostProcessor),则提前生成代理对象。

三级缓存已经解决所有问题了,二级缓存用来做什么呢?为什么三级缓存不直接叫做二级缓存?这个应该是在缓存使用时决定的:

此时这个方法中的判断逻辑是:

  • 一级缓存中没有
  • 对象A确实正在创建中
  • 二级缓存中也没有
  • 最终去三级缓存中获取对象,从三级缓存获取后把对象从三级缓存删除然后放入到二级缓存中,由于当初放入到三级缓存中的是一个工厂,所以从三级缓存中拿对象是调用getEarlyBeanReference这个方法获取,这个方法的作用是如果对象需要代理,那么就返回代理类,如果不需要代理就返回原生类,至此属性注入A完成

那么为什么要把对象从三级缓存放到二级缓存呢?

给大家一个循环依赖的流程图,大家一看便知:

看到了吗,如果AB都需要代理,我们在A的属性注入时创建了B,但是此时A还是原生的A,但是我们需要代理A,而代理A在B注入A时已经创建并放入二级缓存中了,我们直接从二级缓存拿然后替换原生A即可。

所以,我理解的是二级缓存是为了应对代理这个情况而生的
至此,循环依赖的问题已经完美解决

3无法解决的循环依赖

构造函数循环依赖:

如果我们的成员属性是在构造函数里呢?
首先要解决循环依赖就是要先实例化然后放入三级缓存暴露出来,那么如果是构造函数这一步循环依赖,
实例化的时候就会产生无限递归创建,所以不能解决

多例的循环依赖:

如果是多例的,在容器初始化的时候,不会去创建,所以早期没有放入到三级缓存中暴露出来,所以无法解决循环依赖,会报错

到此这篇关于Spring解决循环依赖问题及三级缓存的作用的文章就介绍到这了,更多相关Spring解决循环依赖问题内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Spring源码之循环依赖之三级缓存详解

    目录 循环依赖 定义 三种循环依赖的情况 1.构造器循环依赖 2.settler循环依赖 3.prototype范围的依赖处理 三级缓存机制 整体分析 源码分析 面试题 总结 循环依赖 定义 循环依赖就 循环引用,就是两个或多个 bean 相互之间的持有对方,比如 CircleA 引用 CircleB , CircleB 引用 CircleC, CircleC 引用 CircleA ,则它们最终反映为 个环.此处不是循环调用,循环调用是方法之间的环调用. 循环调用是无法解决的,除非有终结条件,否

  • Spring解决循环依赖的方法(三级缓存)

    说起Spring,作为流水线上装配工的小码农,可能是我们最熟悉不过的一种技术框架.但是对于Spring到底是个什么东西,我猜作为大多数的你可能跟我一样,只知道IOC.DI,却并不明白这其中的原理究竟是怎样的.在这儿你可能想得完整的关于Spring相关的知识,但是我要告诉你对不起.这里不是教程,只能作为你窥探spring核心的窗口.我不做教程,因为网上的教程.源码解析太多,你可以自行选择学习.但我要提醒你的是,看再多的教程也不如你一次的主动去追踪源码. 好了,废话说了这么多就是提醒你这里不是一个教

  • 聊聊Spring循环依赖三级缓存是否可以减少为二级缓存的情况

    基于Spring-5.1.5.RELEASE 问题 都知道Spring通过三级缓存来解决循环依赖的问题.但是是不是必须三级缓存才能解决,二级缓存不能解决吗? 要分析是不是可以去掉其中一级缓存,就先过一遍Spring是如何通过三级缓存来解决循环依赖的. 循环依赖 所谓的循环依赖,就是两个或则两个以上的bean互相依赖对方,最终形成闭环.比如"A对象依赖B对象,而B对象也依赖A对象",或者"A对象依赖B对象,B对象依赖C对象,C对象依赖A对象":类似以下代码: publ

  • 关于Java Spring三级缓存和循环依赖的深入理解

    目录 一.什么是循环依赖?什么是三级缓存? 二.三级缓存如何解决循环依赖? 三.使用二级缓存能不能解决循环依赖? 一.什么是循环依赖?什么是三级缓存? [什么是循环依赖]什么是循环依赖很好理解,当我们代码中出现,形如BeanA类中依赖注入BeanB类,BeanB类依赖注入A类时,在IOC过程中creaBean实例化A之后,发现并不能直接initbeanA对象,需要注入B对象,发现对象池里还没有B对象.通过构建函数创建B对象的实例化.又因B对象需要注入A对象,发现对象池里还没有A对象,就会套娃.

  • 教你Spring如何使用三级缓存解决循环依赖

    一级缓存存放实例化对象 .二级缓存存放已经在内存空间创建好但是还没有给属性赋值的对象.三级缓存存放对象工厂,用来创建提前暴露到bean的对象. @Service public class TestService1 { @Autowired private TestService2 testService2; public void test1() { } } @Service public class TestService2 { @Autowired private TestService1

  • Spring使用三级缓存解决循环依赖的问题

    Spring如何使用三级缓存解决循环依赖在没开始文章之前首先来了解一下什么是循环依赖 @Component public class A { @Autowired B b; } @Component public class B { @Autowired A a; } 在对象A创建过程中,需要注入B,因为容器中没有B,则去创建B,B创建过程中又需要注入A,而A在等待B的创建,B在等待A的创建,导致两者都无法创建成功,无法加入到单例池供用户使用. Spring则通过三级缓存来解决循环依赖的问题,另

  • 一篇文章带你理解Java Spring三级缓存和循环依赖

    目录 一.什么是循环依赖?什么是三级缓存 二.三级缓存如何解决循环依赖? 三.使用二级缓存能不能解决循环依赖? 总结 一.什么是循环依赖?什么是三级缓存 [什么是循环依赖]什么是循环依赖很好理解,当我们代码中出现,形如BeanA类中依赖注入BeanB类,BeanB类依赖注入A类时,在IOC过程中creaBean实例化A之后,发现并不能直接initbeanA对象,需要注入B对象,发现对象池里还没有B对象.通过构建函数创建B对象的实例化.又因B对象需要注入A对象,发现对象池里还没有A对象,就会套娃.

  • 你知道怎么用Spring的三级缓存解决循环依赖吗

    目录 1. 前言 2. Spring Bean的循环依赖 3. Spring中三大循环依赖场景演示 3.1 构造器注入循环依赖 3.2 singleton模式field属性注入循环依赖 3.3 prototype模式field属性注入循环依赖 4. Spring解决循环依赖的原理分析 4.1 Spring创建Bean的流程 4.2 Spring容器的“三级缓存” 4.3 源码解析 4.4 流程总结 5. 总结 1. 前言 循环依赖:就是N个类循环(嵌套)引用. 通俗的讲就是N个Bean互相引用对

  • Spring解决循环依赖问题及三级缓存的作用

    目录 前言 1什么是循环依赖 2 如何解决循环依赖 3无法解决的循环依赖 前言 所谓的三级缓存只是三个可以当作是全局变量的Map,Spring的源码中大量使用了这种先将数据放入容器中等使用结束再销毁的代码风格 Spring的初始化过程大致有四步: 创建beanFactory,加载配置文件 解析配置文件转化beanDefination,获取到bean的所有属性.依赖及初始化用到的各类处理器等 刷新beanFactory容器,初始化所有单例bean 注册所有的单例bean并返回可用的容器 我们说的循

  • 浅谈Spring 解决循环依赖必须要三级缓存吗

    我们都知道 Spring 是通过三级缓存来解决循环依赖的,但是解决循环依赖真的需要使用到三级缓冲吗?只使用两级缓存是否可以呢?本篇文章就 Spring 是如何使用三级缓存解决循环依赖作为引子,验证两级缓存是否可以解决循环依赖. 循环依赖 既然要解决循环依赖,那么就要知道循环依赖是什么.如下图所示: 通过上图,我们可以看出: A 依赖于 B B 依赖于 C C 依赖于 A public class A { private B b; } public class B { private C c; }

  • spring解决循环依赖的简单方法

    Spring内部如何解决循环依赖,一定是单默认的单例Bean中,属性互相引用的场景.比如几个Bean之间的互相引用: 或者 setter方式原型,prototype 原型(Prototype)的场景是不支持循环依赖的,因为"prototype"作用域的Bean,为每一个bean请求提供一个实例,Spring容器不进行缓存,因此无法提前暴露一个创建中的Bean,会抛出异常. 构造器参数循环依赖 Spring容器会将每一个正在创建的Bean 标识符放在一个"当前创建Bean池&q

  • spring解决循环依赖

    概述 循环依赖就是依赖关系形成环,比如最简单的循环依赖:A对象依赖B,B对象依赖A 属性注入与循环依赖 如果是构造器注入,如果循环依赖对象没法构建,因为还未实例化 如果是属性注入但是作用域是prototype,spring不会缓存其对象实例,也不能处理循环依赖的情况 如果是属性注入singleton的,其bean的实例化过程与属性注入过程是分开的,并且spring提供了三个map(就是大家说三级缓存)来实现. spring属性注入处理循环依赖的方式 通过以下xml方式配置一个循环依赖的示例: <

  • 浅谈Spring解决循环依赖的三种方式

    引言:循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错.下面说一下Spring是如果解决循环依赖的. 第一种:构造器参数循环依赖 表示通过构造器注入构成的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyIn CreationException异常表示循环依赖. 如在创建TestA类时,构造器需要TestB类,那将去创建TestB,在创建TestB类时又发现需要TestC类,则又去创建Test

  • Spring为何要用三级缓存来解决循环依赖问题

    我们都知道Spring为了解决循环依赖使用了三级缓存 Spring三级缓存 一级缓存singletonObjects 用于保存BeanName和创建bean实例之间的关系,beanName -> bean instance private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256); 二级缓存earlySingletonObjects 保存提前曝光的单例bean对象 private fin

  • Spring如何解决循环依赖的问题

    前言 在面试的时候这两年有一个非常高频的关于spring的问题,那就是spring是如何解决循环依赖的.这个问题听着就是轻描淡写的一句话,其实考察的内容还是非常多的,主要还是考察的应聘者有没有研究过spring的源码.但是说实话,spring的源码其实非常复杂的,研究起来并不是个简单的事情,所以我们此篇文章只是为了解释清楚Spring是如何解决循环依赖的这个问题. 什么样的依赖算是循环依赖? 用过Spring框架的人都对依赖注入这个词不陌生,一个Java类A中存在一个属性是类B的一个对象,那么我

  • spring 如何解决循环依赖

    首先解释下什么是循环依赖,其实很简单,就是有两个类它们互相都依赖了对方,如下所示: @Component public class AService { @Autowired private BService bService; } @Component public class BService { @Autowired private AService aService; } AService和BService显然两者都在内部依赖了对方,单拎出来看仿佛看到了多线程中常见的死锁代码,但很显然S

随机推荐