Spring循环依赖原理和Bean创建过程

简介: Spring循环依赖原理和Bean创建过程

Spring循环依赖

1.什么是循环依赖

循环依赖就是指Spring在创建Bean的过程中,一个Bean的属性依赖另一个Bean,而被依赖的Bean又依赖于当前Bean,如下图所示:

网络异常,图片无法展示
|


代码如下:

StudentService.java

public class StudentService {
    private TeacherService teacherService;
    //因为要依赖注入,在这里我们先使用setter注入
    public void setTeacherService(TeacherService teacherService) {
        this.teacherService = teacherService;
    }
    static {
        System.out.println("创建StudentServiceBean...");
    }
}
复制代码

TeacherService.java

public class TeacherService {
    private StudentService studentService;
    public void setStudentService(StudentService studentService) {
        this.studentService = studentService;
    }
    static {
        System.out.println("创建teacherServiceBean...");
    }
}
复制代码

测试:

@Test
public void beanTest01() {
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    {
        RootBeanDefinition stu = new RootBeanDefinition(StudentService.class);
        HashMap<String, Object> map = new HashMap<>();
        map.put("teacherService", new RuntimeBeanReference("teacherService"));
        stu.setPropertyValues(new MutablePropertyValues()
                .addPropertyValues(map));
        factory.registerBeanDefinition("studentService", stu);
    }
    {
        RootBeanDefinition tea = new RootBeanDefinition(TeacherService.class);
        HashMap<String, Object> map = new HashMap<>();
        map.put("studentService", new RuntimeBeanReference("studentService"));
        tea.setPropertyValues(new MutablePropertyValues()
                .addPropertyValues(map));
        factory.registerBeanDefinition("teacherService", tea);
    }
    StudentService bean = factory.getBean(StudentService.class);
    System.out.println(bean);
}
复制代码

debug一下看看效果:

网络异常,图片无法展示
|


显然,Spring创建的都是单例的Bean,循环依赖的完成也是由Spring自动进行的,下面我们从创建bean开始来了解Spring是如何解决循环依赖的。

2.Spring创建对象的过程

2.1 大致过程

大致过程无非就是这样:

网络异常,图片无法展示
|


2.2 实际创建过程

详细过程就需要涉及到工厂模式、抽象工厂、单例池…

网络异常,图片无法展示
|


主要类的依赖图(全部为继承关系):

网络异常,图片无法展示
|


  • 首先调用公开方法 DefaultListableBeanFactory.getBean(Class cls) 来获取 Bean 对象,通过一系列操作转化 class 对象为 beanName。
  • 得到 beanName 之后,调用 AbstractBeanFactory.getBean(String name, Class cls) , 在这个方法中又会调用受保护的 AbstractBeanFactory.doGetBean 方法
  • doGetBean 方法中,又会调用 DefaultSingletonBeanFactory.getSingleton(String beanName, ObjectFactory factory)
  • getSingleton 方法中,首先在单例池 DefaultSingletonBeanFactory.singletonObjects 查找是否已经存在 beanName 对应的单例对象。如果找不到,那会调用函数式接口 ObjectFactory 来创建一个对象,而这个对象工厂调用的函数是一个受保护的抽象方法 AbstractBeanFactory.crateBean(String beanName, RootBeanDefinition def, Object[] args)
  • AbstractAutowireCapableBeanFactory 实现了超类的 createBean 方法, 并在 createBean 方法中调用了受保护的方法 AbstractAutowireCapableBeanFactory.doCreateBean
  • doCreateBean方法中,还会调用 AbstractAutowireCapableBeanFactory.populateBean 来填充属性。
  • populateBean 填充属性时,还可能遇上没有创建过的对象,因此还可能递归调用 AbstractBeanFactory.getBean(String beanName)

AbstractAutowireCapableBeanFactory 负责了实例化 Bean,填充属性,后置处理的任务,DefaultSingletonBeanRegistry 负责了单例池的维护工作

3.Spring是如何解决循环依赖的

网络异常,图片无法展示
|


Spring 就是靠获取未完成的Bean对象来填充属性,解决循环依赖的。(这里使用到了三级缓存)

基本过程:

(1)通过getSingleton()时添加,Bean创建状态

(2)实例化Bean之后添加ObjectFactory至工厂池

(3)获取创建状态的Bean时,会先通过ObjectFactory获取Bean,然后添加到半成品池earlySingletonObjects

(4)对象完成创建后会添加到单例池

为什么不直接把对象放入 earlySingletonObjects,而是先放入 singletonFactories 中?

创建对象是需要消耗性能的,假如创建没有循环依赖的对象,是不需要创建未完成的Bean对象的,所以不是直接创建未完成对象放入未完成单例集 earlySingletonObjects,而是把未完成单例工厂放入 singletonFactories

比如以下这段代码,在运行时就不会调用 getSingleton 中的 ObjectFactory.getObject() 方法来创建未完成 Bean 对象。

4.什么情况下不能解决循环依赖

在Bean是多例的状态下不能解决循环依赖(因为按理来说要创建无数个Bean)

试一下:

@Test
public void beanTest01() {
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    {
        RootBeanDefinition stu = new RootBeanDefinition(StudentService.class);
        HashMap<String, Object> map = new HashMap<>();
        map.put("teacherService", new RuntimeBeanReference("teacherService"));
        stu.setPropertyValues(new MutablePropertyValues()
                .addPropertyValues(map));
        stu.setScope("prototype");  //将StudentService设置为多例模式
        factory.registerBeanDefinition("studentService", stu);
    }
    {
        RootBeanDefinition tea = new RootBeanDefinition(TeacherService.class);
        HashMap<String, Object> map = new HashMap<>();
        map.put("studentService", new RuntimeBeanReference("studentService"));
        tea.setPropertyValues(new MutablePropertyValues()
                .addPropertyValues(map));
        factory.registerBeanDefinition("teacherService", tea);
    }
    StudentService bean = factory.getBean(StudentService.class);
    System.out.println(bean);
}
复制代码


报错:

网络异常,图片无法展示
|


参考文章:www.cnblogs.com/kendoziyu/p…


相关文章
|
4月前
|
缓存 Java 开发者
【Spring】原理:Bean的作用域与生命周期
本文将围绕 Spring Bean 的作用域与生命周期展开深度剖析,系统梳理作用域的类型与应用场景、生命周期的关键阶段与扩展点,并结合实际案例揭示其底层实现原理,为开发者提供从理论到实践的完整指导。
552 22
|
4月前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
1528 0
|
3月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
3月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
464 2
|
11月前
|
XML Java 测试技术
Spring IOC—基于注解配置和管理Bean 万字详解(通俗易懂)
Spring 第三节 IOC——基于注解配置和管理Bean 万字详解!
792 26
|
5月前
|
Java 关系型数据库 数据库
深度剖析【Spring】事务:万字详解,彻底掌握传播机制与事务原理
在Java开发中,Spring框架通过事务管理机制,帮我们轻松实现了这种“承诺”。它不仅封装了底层复杂的事务控制逻辑(比如手动开启、提交、回滚事务),还提供了灵活的配置方式,让开发者能专注于业务逻辑,而不用纠结于事务细节。
|
9月前
|
监控 安全 Java
解决 Spring Boot 中 SecurityConfig 循环依赖问题的详解
本文详细解析了在 Spring Boot 中配置 `SecurityConfig` 时可能遇到的循环依赖问题。通过分析错误日志与代码,指出问题根源在于 `SecurityConfig` 类中不当的依赖注入方式。文章提供了多种解决方案:移除 `configureGlobal` 方法、定义 `DaoAuthenticationProvider` Bean、使用构造函数注入以及分离配置类等。此外,还讨论了 `@Lazy` 注解和允许循环引用的临时手段,并强调重构以避免循环依赖的重要性。通过合理设计 Bean 依赖关系,可确保应用稳定启动并提升代码可维护性。
708 0
|
10月前
|
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`两个模块。
431 0
|
9月前
|
存储 人工智能 自然语言处理
RAG 调优指南:Spring AI Alibaba 模块化 RAG 原理与使用
通过遵循以上最佳实践,可以构建一个高效、可靠的 RAG 系统,为用户提供准确和专业的回答。这些实践涵盖了从文档处理到系统配置的各个方面,能够帮助开发者构建更好的 RAG 应用。
3809 114
|
8月前
|
前端开发 Java 数据库连接
Spring核心原理剖析与解说
每个部分都是将一种巨大并且复杂的技术理念传达为更易于使用的接口,而这就是Spring的价值所在,它能让你专注于开发你的应用,而不必从头开始设计每一部分。
235 32