4.3---Spring框架之如何解决循环依赖问题?---(深入版本)

1、什么是循环依赖:

类与类之间的依赖关系形成了闭环,就会导致循环依赖问题的产生

在这里插入图片描述

在这里插入图片描述

代码如下:

public class ClassA {
	private ClassB classB;
 
	public ClassB getClassB() {
		return classB;
	}
 
	public void setClassB(ClassB classB) {
		this.classB = classB;
	}
}
public class ClassB {
	private ClassA classA;
 
	public ClassA getClassA() {
		return classA;
	}
 
	public void setClassA(ClassA classA) {
		this.classA = classA;
	}
}

配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
 
	<bean id="classA" class="ioc.cd.ClassA">
		<property name="classB" ref="classB"></property>
	</bean>
	<bean id="classB" class="ioc.cd.ClassB">
		<property name="classA" ref="classA"></property>
	</bean>
</beans>

测试代码:

	@Test
	public void test() throws Exception {
		// 创建IoC容器,并进行初始化
		String resource = "spring/spring-ioc-circular-dependency.xml";
		ApplicationContext context = new ClassPathXmlApplicationContext(resource);
		// 获取ClassA的实例(此时会发生循环依赖)
		ClassA classA = (ClassA) context.getBean(ClassA.class);
	}

2.循环依赖问题的类型

循环依赖问题在Spring中主要有三种情况:

(1)通过构造方法进行依赖注入时产生的循环依赖问题。
(2)通过setter方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。
(3)通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。

在Spring中,只有第(3)种方式的循环依赖问题被解决了,其他两种方式在遇到循环依赖问题时都会产生异常。

第(1)种构造方法注入的情况下,在new对象的时候就会堵塞住了,其实也就是”先有鸡还是先有蛋“的历史难题。
第(2)种setter方法(多例)的情况下,每一次getBean()时,都会产生一个新的Bean,如此反复下去就会有无穷无尽的Bean产生了,最终就会导致OOM问题的出现。

3.如何解决循环依赖问题?

Spring解决的单例模式下的setter方法依赖注入引起的循环依赖问题,主要是通过两个缓存来解决的:

二级缓存和三级缓存

4.Spring三大缓存介绍

Spring中有三个缓存,用于存储单例的Bean实例,这三个缓存是彼此互斥的,不会针对同一个Bean的实例同时存储。如果调用getBean,则需要从三个缓存中依次获取指定的Bean实例。 读取顺序依次是一级缓存 ,二级缓存,三级缓存。

在这里插入图片描述

1.一级缓存:Map<String, Object> singletonObjects:

(1)第一级缓存的作用:

用于存储单例模式下创建的Bean实例(已经创建完毕)。
该缓存是对外使用的,指的就是使用Spring框架的程序员。
(2)存储什么数据?

K:bean的名称
V:bean的实例对象(有代理对象则指的是代理对象,已经创建完毕)

2.第二级缓存:Map<String, Object> earlySingletonObjects:

第二级缓存的作用:

1.用于存储单例模式下创建的Bean实例(该Bean被提前暴露的引用,该Bean还在创建中)。
2.该缓存是对内使用的,指的就是Spring框架内部逻辑使用该缓存。
3.为了解决第一个classA引用最终如何替换为代理对象的问题(如果有代理对象)

3.第三级缓存:Map<String, ObjectFactory<?>> singletonFactories:

(1)第三级缓存的作用:

1.通过ObjectFactory对象来存储单例模式下提前暴露的Bean实例的引用(正在创建中)。
2.该缓存是对内使用的,指的就是Spring框架内部逻辑使用该缓存。
3.此缓存是解决循环依赖最大的功臣
(2)存储什么数据?

K:bean的名称
V:ObjectFactory,该对象持有提前暴露的bean的引用

为什么第三级缓存要使用ObjectFactory?

在这里插入图片描述

如果对象实现了AOP,那么注入到其他bean的时候,并不是最终的代理对象,而是原始的。这时就需要通过三级缓存的ObjectFactory才能提前产生最终的需要代理的对象。

什么时候将Bean的引用提前暴露给第三级缓存的ObjectFactory持有?

在第一步实例化之后,第二步依赖注入之前,完成此操作。

构造方法进行依赖注入时产生的循环依赖问题如何解决?

使用@Lazy注解来解决,在A或者B的构造函数的形参上加个@Lazy注解实现延迟加载。

@Lazy实现原理是:当实例化对象时,如果发现参数或者属性有@Lazy注解修饰,那么就不直接创建所依赖的对象了,而是使用动态代理创建一个代理类。

比如:

类A的创建:A a=new A(B),需要依赖对象B,发现构造函数的形参上有@Lazy注解,那么就不直接创建B了,而是使用动态代理创建了一个代理类B1,此时A跟B就不是相互依赖了,变成了A依赖一个代理类B1,B依赖A。但因为在注入依赖时,类A并没有完全的初始化完,实际上注入的是一个代理对象,只有当他首次被使用的时候才会被完全的初始化。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值