您现在的位置是:亿华云 > 知识
万字长文带你彻底吃透Spring循环依赖,堪称全网最全(文末福利)
亿华云2025-10-04 03:41:25【知识】3人已围观
简介来源:冰河技术本章难度:★★★★☆本章重点:进一步学习并掌握循环依赖相关的问题,从源码级别彻底掌握Spring解决循环依赖的执行流程。本章目录如下所示:学习指引循环依赖概述循环依赖类型自我依赖直接依赖
来源:冰河技术
本章难度:★★★★☆本章重点:进一步学习并掌握循环依赖相关的字网最问题,从源码级别彻底掌握Spring解决循环依赖的长文称全执行流程。本章目录如下所示:
学习指引循环依赖概述循环依赖类型自我依赖直接依赖间接依赖循环依赖场景单例Bean的彻底吃setter循环依赖的特殊情况多例Bean的setter循环依赖代理对象的setter循环依赖构造方法的循环依赖@DependsOn的循环依赖单例Bean的setter循环依赖Spring循环依赖底层解决方案分析不支持单例Bean的setter循环依赖的特殊情况不支持多例Bean的setter循环依赖不支持代理对象的setter循环依赖不支持构造方法的循环依赖不支持@DependsOn的循环依赖支持单例Bean的setter循环依赖总结思考一、学习指引
Spring中的环依循环依赖问题,你真的赖堪利彻底了解过吗?
Spring的循环依赖问题可以说是面试过程中出现的非常频繁的问题。比如,全文在面试过程中面试官可能会问:有了解过Spring中的末福循环依赖问题吗?或者会问:什么是循环依赖问题?或者会问:Spring中在哪些场景下会产生循环依赖问题?或者会问:Spring是如何解决循环依赖问题的?看似轻描淡写的一句话,网站模板实际要考察的字网最内容也是比较多的。面试官可以通过这个问题考察面试者是长文称全否研究过Spring的源码,有没有了解过Spring中的彻底吃循环依赖问题。
文末扫码加星球立减¥200,环依仅限2023年12月,赖堪利仅限前300名,全文先到先得。末福
本章,字网最我们就一起来聊聊Spring中的循环依赖问题。
二、循环依赖概述
什么是循环依赖?
循环依赖其实也很好理解,可以将这个词拆分成两部分,一个是循环,一个是依赖。循环,顾名思义就是指形成了一个闭合环路,也就是闭环。依赖就是指某个事件的发生要依赖另一个事件。
在Spring中的香港云服务器循环依赖就是指一个或者多个Bean之间存在着互相依赖的关系,并且形成了循环调用。例如,在Spring中,A依赖B,B又依赖A,A和B之间就形成了相互依赖的关系。创建A对象时,发现A对象依赖了B对象,此时先去创建B对象。创建B对象时,发现B对象又依赖了A对象,此时又去创建A对象。创建A对象时,发现A对象依赖了B对象....如果Spring不去处理这种情况,就会发生死循环,一直会创建A对象和B对象,直到抛出异常为止。
同理,在Spring中多个对象之间也有可能存在循环依赖,例如,A依赖B,B依赖C,C又依赖A,A、B、C之间形成了互相依赖的关系,这也是一种循环依赖。亿华云计算
三、循环依赖类型
循环依赖有这些类型呢?
循环依赖总体上可以分成:自我依赖、直接依赖和间接依赖三种类型,如图20-1所示。
3.1 自我依赖
自我依赖就是自己依赖自己,从而形成的循环依赖,一般情况下不会发生这种循环依赖,如图20-2所示。
3.2 直接依赖
直接依赖一般是发生在两个对象之间,例如对象A依赖对象B,对象B又依赖对象A,对象A和对象B之间形成了依赖关系,如图20-3所示。
3.3 间接依赖
间接依赖一般是发生在三个或三个以上对象之间互相依赖的场景,例如对象A依赖对象B,对象B依赖对象C,对象C又依赖对象A,对象A、对象B和对象C之间就形成了循环依赖,如图20-4所示。
四、循环依赖场景
Spring中有哪些循环依赖的场景?
Spring中的循环依赖场景总体上可以分成单例Bean的setter循环依赖、多例Bean的setter循环依赖、代理对象的setter循环依赖、构造方法的循环依赖和DependsOn的循环依赖。如图20-5所示。
4.1 单例Bean的setter循环依赖的特殊情况
Spring是支持基于单例Bean的setter方法的循环依赖的,不过有一种特殊情况需要注意。本节,我们就一起实现基于单例Bean的setter方法的循环依赖的特殊情况,具体实现步骤如下所示。
(1)新增SpecialCircularBeanA类
SpecialCircularBeanA类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.special.bean.SpecialCircularBeanA。
@Componentpublic class SpecialCircularBeanA{
@Autowired privateSpecialCircularBeanB specialCircularBeanB;
@Override public String toString(){
return "SpecialCircularBeanA{ "+
"specialCircularBeanB="+ specialCircularBeanB +
};
}
}
可以看到,在SpecialCircularBeanA类上只标注了@Component注解,所以在IOC容器启动时,会在IOC容器中创建SpecialCircularBeanA类型的单例Bean,并且在SpecialCircularBeanA类型的Bean对象中,会依赖SpecialCircularBeanB类型的Bean对象。同时,在SpecialCircularBeanA类中重写了toString()方法,打印了依赖的SpecialCircularBeanB类型的对象。
(2)新增SpecialCircularBeanB类
SpecialCircularBeanB类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.special.bean.SpecialCircularBeanB。
@Componentpublic class SpecialCircularBeanB{
@Autowired privateSpecialCircularBeanA specialCircularBeanA;
@Override public String toString(){
return "SpecialCircularBeanB{ "+
"specialCircularBeanA="+ specialCircularBeanA +
};
}
}
可以看到,在SpecialCircularBeanB类上只标注了@Component注解,所以在IOC容器启动时,会在IOC容器中创建SpecialCircularBeanB类型的单例Bean,并且在SpecialCircularBeanB类型的Bean对象中,会依赖SpecialCircularBeanA类型的Bean对象。同时,在SpecialCircularBeanB类中重写了toString()方法,打印了依赖的SpecialCircularBeanA类型的对象。
(3)新增SpecialCircularConfig类
SpecialCircularConfig类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.special.config.SpecialCircularConfig。
@Configuration@ComponentScan(value = { "io.binghe.spring.annotation.chapter20.special"})
public class SpecialCircularConfig{
}
可以看到,在SpecialCircularConfig类上标注了@Configuration注解,说明SpecialCircularConfig类是案例程序的配置类,并且使用@ComponentScan注解指定了扫描的包。
(4)新增SpecialCircularTest类
SpecialCircularTest类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.special.SpecialCircularTest。
public class SpecialCircularTest{
public static void main(String[] args){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpecialCircularConfig.class);
SpecialCircularBeanA specialCircularBeanA = context.getBean(SpecialCircularBeanA.class);
System.out.println(specialCircularBeanA);
context.close();
}
}
可以看到,在SpecialCircularTest类的main()方法中,传入SpecialCircularConfig类的Class对象后,创建IOC容器,随后从IOC容器中获取SpecialCircularBeanA类型的Bean对象并进行打印,最后关闭IOC容器。
(5)运行SpecialCircularTest类
运行SpecialCircularTest类的main()方法,输出的结果信息如下所示。
Exception in thread "main"java.lang.StackOverflowError
可以看到,程序抛出了StackOverflowError异常。
其实,从本质上讲,这个异常不是Spring抛出的,而是JVM抛出的栈溢出错误。
4.2 多例Bean的setter循环依赖
Spring是不支持基于多例Bean,也就是原型模式下的Bean的setter方法的循环依赖。本节,我们一起实现一个基于多例Bean的set方法的循环依赖案例,具体实现步骤如下所示。
(1)新增PrototypeCircularBeanA类
PrototypeCircularBeanA类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.prototype.bean.PrototypeCircularBeanA。
@Component@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeCircularBeanA{
@Autowired privatePrototypeCircularBeanB prototypeCircularBeanB;
public PrototypeCircularBeanB getPrototypeCircularBeanB(){
returnprototypeCircularBeanB;
}
}
可以看到,PrototypeCircularBeanA类的对象在Spring中是多例Bean,并且依赖了PrototypeCircularBeanB类型的Bean对象,并提供了getPrototypeCircularBeanB()方法返回PrototypeCircularBeanB类型的Bean对象。
(2)新增PrototypeCircularBeanB类
PrototypeCircularBeanB类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.prototype.bean.PrototypeCircularBeanB。
@Component@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeCircularBeanB{
@Autowired privatePrototypeCircularBeanA prototypeCircularBeanA;
public PrototypeCircularBeanA getPrototypeCircularBeanA(){
returnprototypeCircularBeanA;
}
}
可以看到,PrototypeCircularBeanB类的对象在Spring中是多例Bean,并且依赖了PrototypeCircularBeanA类型的Bean对象,并提供了getPrototypeCircularBeanA()方法返回PrototypeCircularBeanA类型的Bean对象。
(3)新增PrototypeCircularConfig类
PrototypeCircularConfig类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.prototype.config.PrototypeCircularConfig。
@Configuration@ComponentScan(value = { "io.binghe.spring.annotation.chapter20.prototype"})
public class PrototypeCircularConfig{
}
可以看到,在PrototypeCircularConfig类上标注了@Configuration注解,说明PrototypeCircularConfig类是案例程序的配置类,并且使用@ComponentScan注解指定了要扫描的包名。
(4)新增PrototypeCircularTest类
PrototypeCircularTest类的源码详见:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.prototype.PrototypeCircularTest。
public class PrototypeCircularTest{
public static void main(String[] args){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PrototypeCircularConfig.class);
PrototypeCircularBeanA prototypeCircularBeanA = context.getBean(PrototypeCircularBeanA.class);
System.out.println("prototypeCircularBeanA===>>>"+ prototypeCircularBeanA);
System.out.println("prototypeCircularBeanB===>>>"+ prototypeCircularBeanA.getPrototypeCircularBeanB());
context.close();
}
}
可以看到,在PrototypeCircularTest类的main()方法中,创建完IOC容器后,会从IOC容器中获取PrototypeCircularBeanA类型的Bean对象,并打印PrototypeCircularBeanA类型的Bean对象和依赖的PrototypeCircularBeanB类型的Bean对象。
(5)运行PrototypeCircularTest类
运行PrototypeCircularTest类的main()方法,输出的结果信息如下所示。
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name prototypeCircularBeanA: Unsatisfied dependency expressed through field prototypeCircularBeanB: Error creating bean with name prototypeCircularBeanB: Unsatisfied dependency expressed through field prototypeCircularBeanA: Error creating bean with name prototypeCircularBeanA: Requested bean is currently in creation: Is there an unresolvable circular reference?
/很赞哦!(9)
相关文章
- 其次,一般域名注册有一个获取密码的按钮,域名注册商点击后会向您发送密码。在得到域名注册商发送的密码后,将其传输到域名服务提供商网站,然后输入密码,此时域名呈现申请状态。提交申请后,原注册人通常会向您发送一封电子邮件,询问您是否同意转让。此时,您只需点击同意转移按钮,域名注册商就可以成功转移。
- 写一手好 SQL 很有必要
- Java 内存模型,或许应该这么理解
- 一文搞懂各种数据库SQL执行计划:MySQL、Oracle等
- 打开https://www.aizhan.com/输入自己想要查询的域名然后按回车键,如果做过网站都会有数据显示出来
- Redis哨兵的配置和原理
- pw域名怎么样?如何去注册pw域名?
- 在聚名网解析域名怎么操作?
- 第三,.cc域名域名也有很多优势资源域名,从整体注册基数也可以由此推断;
- 注册域名什么样的格式是对的?
热门文章
站长推荐
2. 不要花大价钱买域名,新手鉴别能力不足,容易投资失误。
Python自动化办公小程序:实现报表自动化和自动发送到目的邮箱
Map+函数式接口方法 优雅的解决 if-else
Ruby vs Golang:性能、社区、兼容性和开发经验,谁更胜一筹?
为了避免将来给我们的个人站长带来的麻烦,在选择域名后缀时,我们的站长最好省略不稳定的后缀域名,比如n,因为我们不知道策略什么时候会改变,更不用说我们将来是否还能控制这个域名了。因此,如果站长不是企业,或者有选择的话,如果不能选择域名的cn类,最好不要选择它。
听说Redis都会遇到并发、雪崩等难题?我用10分钟就解决了
读过本文才算真正了解Cassandra数据库
MySQL常用优化指南,及大表优化思路(值得收藏)