您现在的位置是:亿华云 > 知识
万字长文带你彻底吃透Spring循环依赖,堪称全网最全(文末福利)
亿华云2025-10-09 03:58:28【知识】0人已围观
简介来源:冰河技术本章难度:★★★★☆本章重点:进一步学习并掌握循环依赖相关的问题,从源码级别彻底掌握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中的云南idc服务商循环依赖就是指一个或者多个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?
/很赞哦!(918)
相关文章
- 3.dns修改成功后,点击“域名解析”,按提示进行操作。解析格式一般如下:
- Web 的这 26 项基本概念和技术,你知道吗?(上)
- 深入理解 nvidia-docker 2.0
- 怎么能选择合适的域名?需要怎么做?
- 3、查看排名
- Django报错ValueError: invalid literal for int() with base 10:
- 如何优雅使用Docker?请收下这15个小技巧。
- 聚名网新上.net续费/转入福利包,.cn域名续费/转入低至37元/年!
- 投资各类域名就像到处打游击战,结果处处失败。因为这样,对任何一个中国域名市场的走势和价格都没有准确的把握,所以最好缩小范围,准确把握战场态势,埋伏。
- Hive 内置的 Json 解析函数