【spring】06 循环依赖的分析与解决

简介: 【spring】06 循环依赖的分析与解决

什么是循环依赖?

@Component
public class CircleA {
 @Autowired
 private CircleB B;
}

@Component
public class CircleB {
 @Autowired
 private CircleA  A;
}

可以看出CircleA 了CircleB ,CircleB 注入了CircleA,循环依赖就是循环引用,就是两个或多个bean相互之间的持有对方

循环依赖什么时候可以解决?

出现循环依赖的Bean必须要是单例

这个很好理解,就是类需要是单例的

依赖注入的方式不能全是构造器注入的方式

@Component
public class A {
// @Autowired
// private B b;
 public A(B b) {

 }
}


@Component
public class B {

// @Autowired
// private A a;

 public B(A a){

 }
}

以上代码中,B是A构造方法的参数,A是B构造方法的参数,这种依赖注入的方式全是构造器注入的方式

启动的话会报错

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
依赖情况 依赖注入方式 循环依赖是否被解决
AB相互依赖(循环依赖 ) 均采用setter方法注入
AB相互依赖(循环依赖) 均采用构造器注入
AB相互依赖(循环依赖) A中注入B的方式为setter方法 ,B中注入A的方式为构造器
AB相互依赖(循环依赖) B中注入A的方式为setter方法 ,A中注入B的方式为构造器

Spring是如何解决的循环依赖?

先说总结,Spring通过三级缓存的方式解决循环依赖,假设A ref B,B ref A。用户第一次尝试getBean(A),发现没有这个bean就去初始化,初始化的时候一开始就把这个A的实例放进容器,然后才开始setProperties,这就会触发B的实例的初始化。创建B的bean的时候赋值属性的时候发现需要一个A的bean,可以直接从容器中获取,虽然A的bean只是一个空架子没有完成初始化,但是已经可以引用了

源码分析

还是以上述的A B为例,创建一个bean就是分下面两种情况,如果这个bean在缓存中没有,就新建,如果三级缓存中有,就去三级缓存中去拿

  • 创建一个新的Bean
  • 从缓存中获取到已经被创建的对象

是不是跟redis经典策略有点像?缓存中有就拿缓存的,如果没有就去数据库拿,然后刷新缓存。

接下来看一下源码

// 创建bean会调用getSingleton方法
public Object getSingleton(String beanName) {
    return getSingleton(beanName, true); // getSingleton会去三级缓存中获取bean
}

整个缓存一共有三级

  1. singletonObjects,一级缓存,存储的是所有创建好了的单例Bean
  2. earlySingletonObjects,完成实例化,但是还未进行属性注入及初始化的对象
  3. singletonFactories,提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象

如果A是第一次创建,那么这三个缓存中都没有A

如果都没有就会调用另外一个重载方法getSingleton

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    // 这里加了synchronized 锁
    synchronized (this.singletonObjects) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            // 在单例对象创建前先做一个标记
            // 将beanName放入到singletonsCurrentlyInCreation这个集合中
            // 标志着这个单例Bean正在创建
            // 如果同一个单例Bean多次被创建,这里会抛出异常
            beforeSingletonCreation(beanName);// 创建bean之前
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                // 上游传入的lambda在这里会被执行,调用createBean方法创建一个Bean后返回
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            // ...
            // 省略catch异常处理
            // ...
            finally {
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }
                // 创建完成后将对应的beanName从singletonsCurrentlyInCreation移除
                afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                // 添加到一级缓存singletonObjects中
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}

从下面的代码就可以分析出,一级缓存中放的是完全创建好了的单例Bean

         if (newSingleton) {
                // 添加到一级缓存singletonObjects中
                addSingleton(beanName, singletonObject);
            }

因为B需要注入A,所以在创建B的时候,又会去调用getBean(a),这个时候就又回到之前的流程了,但是不同的是,之前的getBean是为了创建Bean,而此时再调用getBean不是为了创建了,而是要从缓存中获取,因为之前A在实例化后已经将其放入了三级缓存singletonFactories中,所以此时getBean(a)的流程就是这样子了

问题

之前的问题就是A创建完毕之后,属性注入会创建B,B创建完毕之后又会创建A,然后A又创建B这样无限循环

解决

综上,A创建时没有a对象,就会去创建a对象,然后放入三级缓存,然后B创建的时候会创建A,创建A时会先去缓存中查询是否有A,有就直接使用缓存中的,不会再去创建

目录
相关文章
|
监控 安全 Java
解决 Spring Boot 中 SecurityConfig 循环依赖问题的详解
本文详细解析了在 Spring Boot 中配置 `SecurityConfig` 时可能遇到的循环依赖问题。通过分析错误日志与代码,指出问题根源在于 `SecurityConfig` 类中不当的依赖注入方式。文章提供了多种解决方案:移除 `configureGlobal` 方法、定义 `DaoAuthenticationProvider` Bean、使用构造函数注入以及分离配置类等。此外,还讨论了 `@Lazy` 注解和允许循环引用的临时手段,并强调重构以避免循环依赖的重要性。通过合理设计 Bean 依赖关系,可确保应用稳定启动并提升代码可维护性。
1000 0
|
Java Maven 微服务
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的 maven 依赖
在项目中使用Swagger2工具时,需导入Maven依赖。尽管官方最高版本为2.8.0,但其展示效果不够理想且稳定性欠佳。实际开发中常用2.2.2版本,因其稳定且界面友好。以下是围绕2.2.2版本的Maven依赖配置,包括`springfox-swagger2`和`springfox-swagger-ui`两个模块。
633 0
|
负载均衡 Java API
基于 Spring Cloud 的微服务架构分析
Spring Cloud 是一个基于 Spring Boot 的微服务框架,提供全套分布式系统解决方案。它整合了 Netflix、Zookeeper 等成熟技术,通过简化配置和开发流程,支持服务发现(Eureka)、负载均衡(Ribbon)、断路器(Hystrix)、API网关(Zuul)、配置管理(Config)等功能。此外,Spring Cloud 还兼容 Nacos、Consul、Etcd 等注册中心,满足不同场景需求。其核心组件如 Feign 和 Stream,进一步增强了服务调用与消息处理能力,为开发者提供了一站式微服务开发工具包。
967 0
|
缓存 架构师 Java
图解 Spring 循环依赖,一文吃透!
Spring 循环依赖如何解决,是大厂面试高频,本文详细解析,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
图解 Spring 循环依赖,一文吃透!
|
SQL 前端开发 Java
深入分析 Spring Boot 项目开发中的常见问题与解决方案
本文深入分析了Spring Boot项目开发中的常见问题与解决方案,涵盖视图路径冲突(Circular View Path)、ECharts图表数据异常及SQL唯一约束冲突等典型场景。通过实际案例剖析问题成因,并提供具体解决方法,如优化视图解析器配置、改进数据查询逻辑以及合理使用外键约束。同时复习了Spring MVC视图解析原理与数据库完整性知识,强调细节处理和数据验证的重要性,为开发者提供实用参考。
544 0
|
缓存 Java 应用服务中间件
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——依赖导入和Thymeleaf相关配置
在Spring Boot中使用Thymeleaf模板,需引入依赖`spring-boot-starter-thymeleaf`,并在HTML页面标签中声明`xmlns:th=&quot;http://www.thymeleaf.org&quot;`。此外,Thymeleaf默认开启页面缓存,开发时建议关闭缓存以实时查看更新效果,配置方式为`spring.thymeleaf.cache: false`。这可避免因缓存导致页面未及时刷新的问题。
522 0
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
249 14
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
4678 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
396 2
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
1959 2