看现象
maven依赖
我们只测试IOC容器,因此只需要引入spring-context
即可
<dependencies>
<!--测试框架-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<!--假数据声场-->
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>1.0.2</version>
</dependency>
<!--IOC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>
测试用例
public class TestSpringIOCCircularDependency {
@Test
public void test(){
// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("ioc/circularDependency.xml");
// retrieve configured instance
CircleA circleA = context.getBean("circleA", CircleA.class);
circleA.test();
CircleB circleB = context.getBean("circleB", CircleB.class);
circleB.test();
}
}
测试用例里面引入了配置文件:circularDependency.xml
,看看里面写了啥
circularDependency.xml
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--来一个普通bean,看看普通bean与循环引用的bean构建过程有什么不同-->
<bean id="userDao" class="com.spring.dao.impl.UserDaoImpl"/>
<!--A依赖于B,B依赖于A,形成循环依赖-->
<bean id="circleA" class="com.spring.service.CircleA">
<property name="circleB" ref="circleB"/>
</bean>
<bean id="circleB" class="com.spring.service.CircleB">
<property name="circleA" ref="circleA"/>
</bean>
</beans>
CircleA
/**
* A依赖B
*/
public class CircleA {
static {
System.out.println("I AM CircleA , I am loaded now @" + System.currentTimeMillis());
}
/*注入circleB*/
private CircleB circleB;
public void setCircleB(CircleB circleB) {
this.circleB = circleB;
}
public void test(){
System.out.println("I am CircleA , I have an circleB@" + circleB.hashCode());
}
}
CircleB
/**
* B依赖A
*/
public class CircleB {
static {
System.out.println("I AM CircleB , I am loaded now @" + System.currentTimeMillis());
}
/*注入circleA*/
private CircleA circleA;
public void setCircleA(CircleA circleA) {
this.circleA = circleA;
}
public void test(){
System.out.println("I am CircleB , I have an circleA@" + circleA.hashCode());
}
}
运行结果
I AM CircleA , I am loaded now @1615430548511
I AM CircleB , I am loaded now @1615430548512
I am CircleA , I have an circleB@428910174
I am CircleB , I have an circleA@1682463303
思考
相信你一眼就能看出来CircleA
依赖CircleB
,而CircleB
创建的时候又要依赖于CircleA
,这不就是典型的循环依赖么?但是Spring在创建的时候并不会发生死循环,它又是怎么处理的?
Spring IOC如何解决循环依赖的问题?
创建过程
普通对象的创建
一上来先调用addSingletonFactory
扔到三级缓存里面,创建完成后调用addSingleton
转移到一级缓存里面,普通无循环依赖的对象就算创建完毕了
循环依赖对象的创建
- A 创建过程中需要 B,于是将A自己放到三级缓存里面 ,去实例化 B(在实例化B的时候,也会把B放到三级缓存里面)
- B 实例化的时候发现需要 A,于是调用
getSingleton
方法,先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了!(也就是说A发现创建自己需要依赖自己)- 然后把三级缓存里面的这个 A 放到二级缓存里面,并删除三级缓存里面的 A
- B 顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态)
- 然后回来接着创建 A,此时 B 已经创建结束,直接从一级缓存里面拿到 B ,然后完成A的创建,清空三级缓存,并将自己放到一级缓存里面,如此一来便解决了循环依赖的问题
总而言之,通过getSingleton
方法,会检测到创建一个bean对象是否在某种依赖深度上需要依赖自己,如果依赖(即三级缓存里面有自己)那就是循环引用
三级缓存
四哥们儿,三个map一个set,在类DefaultSingletonBeanRegistry
中定义的:
singletonObjects
:单例缓存,也叫第一级缓存earlySingletonObjects
:早期单例缓存(啥叫早期?就是说这个对象里面的属性还没有被填充的),也叫第二级缓存singletonFactories
:单例工厂缓存,也叫第三级缓存singletonsCurrentlyInCreation
:是个set,用于帮助判断当前bean是否在创建中,后文有讲
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
三个方法
这几个方法保证了一个bean在三级缓存中只能在一个缓存中存在,并且都加了锁是线程安全的,这几个缓存表示了bean创建的不同状态,也是在类DefaultSingletonBeanRegistry
中定义的。
- 添加单例(一级缓存):
确保只有一级缓存才有!此时说明单列已经创建完毕,里面的属性也填充好了,往singletonObjects
单列缓存里面加东西,其他的缓存全部清掉,registeredSingletons
当然也要加上,注意是set,多次添加也无妨
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
- 添加单例工厂缓存(三级缓存):
确保只有三级缓存才有!在单列缓存singletonObjects
没有的时候才能添加,如果bena都全部初始化好了,那没有必要再往单例工厂缓存里面添加了
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);
}
}
}
并且在下面这种条件下添加,即要创建的bena是单列,并且允许循环引用,并且当前对象正在创建中...,allowCircularReferences
参照AbstractAutowireCapableBeanFactory.allowCircularReferences = true
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
- 拿到单列
三级缓存转移到二级缓存!重点来了,如果是循环引用,此处有一个三级缓存转移到二级缓存的过程
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
//在一级缓存没拿到,说明bean还没创建好并且当前bean正在被创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//二级缓存去拿
singletonObject = this.earlySingletonObjects.get(beanName);
//二级缓存没拿到就去三级缓存拿
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
//三级缓存拿到了就转移到二级缓存
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
一个标志位
这个方法DefaultSingletonBeanRegistry.isSingletonCurrentlyInCreation
返回当前传入对象是否正在创建,那么当在什么时候在创建中?什么时候创建完毕呢?
下面的方法都在DefaultSingletonBeanRegistry
中定义的
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
singletonsCurrentlyInCreation
这玩意定义是下面这样子的,就是个set,往里面放的就是在创建中的,移出去的就是创建完成来的,那么我们只需要找在一个bean创建过程当中什么时候往里面放入了,什么时候又从里面移除掉了
为啥这个set要定义成这样,new个set不好?这里肯定是为了线程安全,是从ConcurrentHashMap转换成set的
/** Names of beans that are currently in creation. */
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
在这里放入的,在加入三级缓存之前就放入了
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
在这里移除的
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}