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);
}

报错:


相关文章
|
25天前
|
XML 安全 Java
|
2天前
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
|
2天前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
10天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
55 14
|
8天前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
40 6
|
10天前
|
XML Java 数据格式
🌱 深入Spring的心脏:Bean配置的艺术与实践 🌟
本文深入探讨了Spring框架中Bean配置的奥秘,从基本概念到XML配置文件的使用,再到静态工厂方式实例化Bean的详细步骤,通过实际代码示例帮助读者更好地理解和应用Spring的Bean配置。希望对你的Spring开发之旅有所助益。
59 3
|
23天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
44 2
|
23天前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
32 1
|
Java Spring
Spring原理学习系列之五:IOC原理之Bean加载
其实很多同学都想通过阅读框架的源码以汲取框架设计思想以及编程营养,Spring框架其实就是个很好的框架源码学习对象。我们都知道Bean是Spring框架的最小操作单元,Spring框架通过对于Bean的统一管理实现其IOC以及AOP等核心的框架功能,那么Spring框架是如何把Bean加载到环境中来进行管理的呢?本文将围绕这个话题进行详细的阐述,并配合Spring框架的源码解析。
Spring原理学习系列之五:IOC原理之Bean加载
|
2月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
245 2