首页 > 编程语言 >spring源码 循环依赖

spring源码 循环依赖

时间:2024-07-30 21:27:30浏览次数:12  
标签:缓存 依赖 对象 spring beanName bean 源码 创建 属性

spring框架两大核心:IOC和AOP

IOC(Inverse of Control)控制反转

将对象的创建权交给 Spring 容器去创建,利用了工厂模式将对象交给容器管理,只需要在spring配置文件中配置相应的bean,以及设置相关的属性,让spring容器来生成类的实例对象以及管理对象。不需要我们手动new去创建对象,大大降低了代码间的耦合度,使资源更加容易管理。

AOP(Aspect Oriented Programming)面向切面编程

通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。

循环依赖

问题描述

A有属性b,B有属性a,创建A时对b属性赋值需要有b对象,如果没有b就创建,但是创建B又需要对属性a赋值,这样就构成了循环依赖

前置知识

Spring在创建Bean的过程中分为三步

实例化 ,对应方法:AbstractAutowireCapableBeanFactory中的createBeanInstance方法

属性注入,对应方法:AbstractAutowireCapableBeanFactorypopulateBean方法

初始化,对应方法: AbstractAutowireCapableBeanFactoryinitializeBean方法

其中AOP是在初始化阶段完成的

解决方案:三级缓存(3个Map)

核心思想是:bean进行实例化操作后,不需要进行依赖注入(属性赋值)操作就放入缓存中,当其他bean需要时直接从缓存中拿就行了

  • 一级缓存为:singletonObjects 缓存已经创建好的bean对象(完成了实例化,属性注入,初始化)。
  • 二级缓存为:earlySingletonObjects 缓存只完成了实例化,但是还未进行属性注入及初始化的对象
  • 三级缓存为:singletonFactories 提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象

利用三级缓存解决循环依赖的流程(假设A对象中有属性b,B对象中有属性a)

首先创建A对象,先后查询一级,二级,三级缓存,都没有A这个bean对象,所以先进行实例化,实例化完成后存到三级缓存中,然后进行属性注入,A类只有一个类型为B的属性b,先后查询一级,二级,三级缓存,都没有B这个bean对象,先实例化,实例化后存到三级缓存中,进行属性注入,B类只有一个类型为A的属性a,查一级缓存,没有,查二级缓存,没有,查三级缓存,找到提前暴露的工厂,通过工厂创建A对象,将三级缓存中的创建A对象的工厂删除(remove),将创建好的A对象存进二级缓存,然后赋值给B类的a属性,B类进行初始化,此时已经创建完成,就赋值给A类的b属性,A类进行初始化,创建完成。

现在有这样一个经典的循环依赖(先不考虑AOP)

执行流程:

首先是创建A:

在getSingleton(beanName,true)方法中,依次查询一二三级缓存,发现都没有beanName为A的映射,就跳转到重载方法getSingleton(beanNeme, singletonFactory)中,这个方法的用途是创建bean,因为缓存中都没有,所以要新创建这个A对象。但是并不是简单调用createBean这个方法就完了,还要将创建好的bean(完成实例化,属性注入,初始化)存进一级缓存singletonObjects

来看下getSingleton(beanName,singletonFactory)方法源码:(看蓝色重点部分)

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    synchronized (this.singletonObjects) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {

            // ....
            // 省略异常处理及日志
            // ....

            // 在单例对象创建前先做一个标记
            // 将beanName放入到singletonsCurrentlyInCreation这个集合中
            // 标志着这个单例Bean正在创建
            // 如果同一个单例Bean多次被创建,这里会抛出异常
            beforeSingletonCreation(beanName);
            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;
    }
}

这里的getObject()方法会调用createBean方法

真正完成这些的是doCreateBean方法

在doCreateBean方法中:

先进行实例化,实例化后对实例化后的bean对象封装成一个对象工厂,存进三级缓存singletonFactorys,然后才进行属性注入和初始化

// Eagerly cache singletons to be able to resolve circular references
        // even when triggered by lifecycle interfaces like BeanFactoryAware.
        //向容器中缓存单例模式的Bean对象,以防循环引用
        //判断是否是早期引用的bean,如果是,则允许其提前暴露引用
        // 这里判断的逻辑主要有三个∶
        //1.是否为单例
        //2 是否允许循环引用
        //3.是否是在创建中的bean
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            if (logger.isTraceEnabled()) {
                logger.trace("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
            }
            //这里是一个匿名内部类,为了防止循环引用,尽早持有对象的引用
            //将还没完全配置好的bean存入到三级缓存中供其他bean使用(暴露引用)
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
            //这里的getEarlyBeanReference()方法,并不是在此调用,而是声明好了方法
            //具体的调用在Spring的AbstractBeanFactory的doGetBean的第三行调用
            // DefaultSingletonBeanRegistry类的getSingleton方法中,调用
            // singletonFactory.getObject()(单例工厂的getObject方法返回实例对象)
        }
 
        // Initialize the bean instance.
        //Bean对象的初始化,依赖注入在此触发
        //这个exposedObject在初始化完成之后返回作为依赖注入完成后的Bean
        Object exposedObject = bean;//暴露的对象
        try {
            //属性注入
            populateBean(beanName, mbd, instanceWrapper);
 
            //初始化bean,过程如下:
            //1.判断是否实现了BeanNameAware,BeanClassLoaderAware,BeanFactoryAware方法,
            //如果有,则设置相关的属性
            //2.调用bean初始化前的前置(BeanPostProcessor)操作
            //3.执行初始化的方法
            //如果有InitializingBean,则调用afterPropertiesSet
            //如果有InitMethod,则调用初始方法
            //4.调用bean初始化的后置(BeanPostProcessor)操作
            
            //初始化
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }

这里将实例化后的bean添加到三级缓存singletonFactorys调用了方法:

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

这里的第二个参数是一个lambda表达式 () -> getEarlyBeanReference(beanName, mbd, bean),

这个addSingletonFactory是什么呢?

在通过三级缓存得到beanName对应的工厂后(objectFactory),调用getObject方法来得到对象,这个对象实际上就是通过getEarlyBeanReference方法创建的。

addSingletonFactory方法源码:

// 这里传入的参数也是一个lambda表达式,() -> getEarlyBeanReference(beanName, mbd, bean)
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            // 添加到三级缓存中
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

addSingletonFactory方法源码:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    //如果要实现AOP
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

回到流程中

此时A这个bean已经实例化好并且存进三级缓存singletonFactorys中,下一步是属性注入,A中只有B类型的b这一个属性,和创建A时一样,还是

getBean->doGetBean->getSingleton(bean,true)->getSingleton(b,ObjectFactory)

->createBean->doCreateBean

实例化后存到三级缓存中,进行属性注入,B里面只有A类型的a这一个属性,依次查询一二三级缓存,然后在第三级缓存中查询到A,得到A的工厂,调用getObject()方法得到只进行了实例化的a,将a赋值给B,再进行初始化,此时B已经创建完成,将B赋值给A的b属性,A进行初始化,此时A也创建完成。

这里还要注意:从三级缓存中获取完之后,会将工厂得到的对象存进二级缓存earlySingletonObjects,然后再三级缓存中删除该beanName对应的映射。

考虑AOP的情况

如果启用了AOP,那么在A的创建过程中:

在实例化后进行存入三级缓存时,存的是代理后的对象

在B的创建过程中:

在属性注入时,从三级缓存中获取A得到的也是代理后的对象

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}
public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);
    // 如果需要代理,返回一个代理对象,不需要代理,直接返回当前传入的这个bean对象
    return wrapIfNecessary(bean, beanName, cacheKey);
}

标签:缓存,依赖,对象,spring,beanName,bean,源码,创建,属性
From: https://blog.csdn.net/Dennis_nafla/article/details/140807100

相关文章

  • 环境变量和python多版本共存,视图层源码分析,视图层总结,路由层,
    Ⅰ环境变量和python多版本共存【一】环境变量【1】什么是环境变量无论是win,mac,linux都有环境变量的概念,以win为例什么是环境变量?环境变量(environmentvariables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,如:临时文件夹位置和系统文件夹位置等。环境变量是在......
  • Spring AOP概念及原理
    SpringAOP(面向切面编程)以下内容由ChatGPT生成AOP(Aspect-OrientedProgramming,面向切面编程)是一种编程范式,旨在通过分离关注点来提高程序的模块化。SpringAOP主要用于横切关注点(如日志记录、安全、事务管理等)的实现。在Spring中,AOP的主要功能是为Bean增强功能,如添加额外......
  • springboot+vue基于微服务架构的设备管理系统【程序+论文+开题】-计算机毕业设计
    系统程序文件列表开题报告内容研究背景随着企业规模的不断扩大与信息化程度的日益加深,设备管理成为企业运营中不可或缺的一环。传统集中式架构的设备管理系统在面对大规模数据处理、高并发访问及系统扩展性等方面显得力不从心。微服务架构以其高度的模块化、灵活的服务部署......
  • 基于SpringBoot+Vue的电影院订票信息管理系统的详细设计和实现(源码+lw+部署文档+讲解
    文章目录前言详细视频演示项目运行截图技术框架后端采用SpringBoot框架前端框架Vue可行性分析系统测试系统测试的目的系统功能测试数据库表设计代码参考数据库脚本为什么选择我?获取源码前言......
  • 基于SpringBoot+Vue的甘肃旅游管理系统的详细设计和实现(源码+lw+部署文档+讲解等)
    文章目录前言详细视频演示项目运行截图技术框架后端采用SpringBoot框架前端框架Vue可行性分析系统测试系统测试的目的系统功能测试数据库表设计代码参考数据库脚本为什么选择我?获取源码前言......
  • 记一次解决SpringBoot项目由于依赖加载顺序问题导致启动报NoSuchMethodError的问题
    只发博客园,盗版必究先说背景平时我们的SpringBoot项目都是打成ExecutableJar启动应用,最近接了个技术需求,需要打成War包,将多个项目放在同一个Tomcat中运行。原本Jar包启动一切正常,但是打成WAR放Tomcat启动后报错了,异常栈如下:Causedby:org.springframework.beans.factory.......
  • [附开题]flask框架的汽车零件维修管理信息平台t6rr1(python+源码)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着汽车工业的快速发展和汽车保有量的持续增长,汽车零件维修管理已成为汽车后市场的重要组成部分。传统的手工记录和管理方式已难以满足现......
  • [附开题]flask框架的汽车售后管理系统2888o(python+源码)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着汽车产业的快速发展和消费者购车需求的日益增长,汽车售后服务已成为车企和经销商提升客户满意度、增强品牌忠诚度的重要环节。然而,传统......
  • [附开题]flask框架的社区服务管理系统0f6i9(python+源码)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着城市化进程的深入和居民生活水平的提高,社区服务需求日益多样化与复杂化。传统的社区服务模式已难以满足居民对于高效、便捷、个性化服......
  • [附开题]flask框架的社区服务系统ff00q(python+源码)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着社会的快速发展和居民生活水平的提高,社区作为城市的基本单元,其服务功能日益丰富和多样化。然而,传统社区服务方式在应对日益增长的居民......