开发者

Spring循环依赖​​的解决方式详解

开发者 https://www.devze.com 2025-05-11 10:25 出处:网络 作者: 二进制11
目录什么是编程循环依赖Spri编程客栈ng解决循环依赖的机制三级缓存结构解决流程详细步骤解析代码层面的实现循环依赖的限制最佳实践总结什么是循环依赖
目录
  • 什么是编程循环依赖
  • Spri编程客栈ng解决循环依赖的机制
    • 三级缓存结构
    • 解决流程
    • 详细步骤解析
    • 代码层面的实现
  • 循环依赖的限制
    • 最佳实践
      • 总结

        什么是循环依赖

        循环依赖是指两个或多个Bean相互依赖,形成一个闭环。例如:

        @Component
        public class A {
            @Autowired
        编程客栈    private B b;
        }
        @Component
        public class B {
            @Autowired
            private A a;
        }

        在这个例子中,A依赖B,B又依赖A,形成了一个循环依赖链。

        Spring解决循环依赖的机制

        Spring通过三级缓存机制来解决循环依赖问题,具体实现如下:

        三级缓存结构

        • 一级缓存(singletonObjects):存储完全初始化好的Bean
        • 二级缓存(earlySingletonObjects):存储提前暴露的原始Bean(尚未填充属性)
        • 三级缓存(singletonFactories):存储Bean工厂,用于生成原始Bean的早期引用

        Spring循环依赖​​的解决方式详解

        解决流程

        Spring解决循环依赖的核心流程如下:

        Spring循环依赖​​的解决方式详解

        详细步骤解析

        创建Bean A:

        • Spring容器开始创建Bean A
        • 实例化A(调用构造方法)
        • 将A的ObjectFactory放入三级缓存(singletonFactories)
        • 准备填充A的属性

        发现依赖B:

        • 在填充A的属性时,发现需要注入Bean B
        • 容器开始创建Bean B

        创建Bean B:

        • 实例化B(调用构造方法)
        • 将B的ObjectFactory放入三级缓存(singletonFactories)
        • 准备填充B的属性

        发现依赖A:

        • 在填充B的属性时,发现需要注入Bean A
        • 容器尝试从一级缓存获取A(未找到)
        • 从二级缓存获取A(未找到)
        • 从三级缓存获取A的ObjectFactory并调用getObject()获取早期引用
        • 将A的早期引用放入二级缓存,并从三级缓存移除

        完成B的创建:

        • 将B的属性填充完整
        • 执行B的初始化后处理器
        • 将完整的B放入一级缓存
        • 从二级和三级缓存中移除B的相关条目

        完成A的创建:

        • 现在A可以获取到完整的B实例
        • 将A的属性填充完整
        • 执行A的初始化后处理器
        • 将完整的A放入一级缓存
        • 从二级缓存中移除A

        代码层面的实现

        Spring解决循环依赖的核心代码在DefaultSingletonBeanRegistry类中:

        protected Object getSingleton(String beanName, boolean allowEarlyReference) {
            // 首先检查一级缓存
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
                synchronized (this.singletonObjects) {
                    // 检查二级缓存
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    ijsf (singletonObject == null && allowEarlyReference) {
                        // 检查三级缓存
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject();
                            // 从三级缓存移到二级缓存
                        php    this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
            return singletonObject;
        }

        循环依赖的限制

        Spring并非能解决所有类型的循环依赖,有以下限制:

        构造器注入:无法解决构造器注入的循环依赖

        @Component
        public class A {
            private B b;
            @Autowired
            public A(B b) { this.b = b; }
        }
        @Component
        public class B {
            private A a;
            @Autowired
            public B(A a) { this.a = a; }
        }

        这种情况会抛出BeanCurrentlyInCreationException

        原型(prototype)作用域的Bean:Spring不支持原型Bean的循环依赖

        @Async方法:如果循环依赖中包含@Async方法,也可能出现问题

        最佳实践

        • 尽量避免循环依赖,设计时应考虑解耦
        • 如果必须使用循环依赖,优先使用setter注入而非构造器注入
        • 考虑使用@Lazy注解延迟加载其中一个Bean
        @Component
        public class A {
            @Lazy
            @Autowired
            private B b;
        }

        总结

        Spring通过三级缓存机制巧妙地解决了setter/field注入方式的循环依赖问题。理解这一机制不仅有助于我们避免开发中的循环依赖陷阱,也能更深入地理解Spring容器的Bean生命周期管理。在实际开发中,我们应当合理设计Bean之间的依赖关系,尽量避免循环依赖,当确实需要时,也要了解其背后的原理和限制。

        以上就是Spring循环依赖​​的解决方式详解的详细内容,更多关于Spring循环依赖的资料请关注编程客栈(www.devze.com)其它相关文章!

        0

        精彩评论

        暂无评论...
        验证码 换一张
        取 消

        关注公众号