问题引入
Spring的循环引用
有时候我们可能会涉及到两个bean对象互相引用的情况,通过对bean的实例化过程的了解,我们知道spring会先将这个bean对象所有的实例属性填充完毕,才会将这个bean实例化。于是这样便会产生一个死锁问题。对于这种情况,spring内部采用了三级缓存机制来解决上述问题。
举个例子
<!--循环引用-->
<bean name="UserDao" class="com.cjh.dao.impl.UserDaoImpl">
<property name="UserService" ref="UserService"></property>
</bean>
<bean name="UserService" class="com.cjh.service.impl.UserServiceImpl">
<property name="UserDao" ref="UserDao"></property>
</bean>
三级缓存
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
//缓存最终单例bean成品的容器,即储存实例化和初始化都完成的bean,被称为一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
//早期Bean单例池,用于缓存已经被其他对象引用过的半成品对象,被称为二级缓存
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
//单例Bean的工厂池,缓存未被引用的半成品对象,使用时通过工厂创建Bean,被称为三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
}
运用三级缓存解决循环依赖的过程
借用一下上面的UserService和UserDao互相依赖的例子。
因为我们的UserService配在UserDao的上面,我们会先加载UserService。
首先先实例化UserService,但是还未初始化,将其放入我们的三级缓存中。
之后我们为UserService注入属性,发现需要UserDao,到缓存中找,发现没有。
实例化UserDao,未完成初始化,将其放入三级缓存中。
UserDao进行属性注入,发现需要UserService,在三级缓存中找到,将其获取,并将其移入二级缓存
UserDao执行其他的生命周期过程,成为一个完整的bean,存到一级缓存,删除二三级缓存。
UserService注入UserDao
UserService执行其他的生命周期过程,成为一个完整的bean,删除二三级缓存。
三级缓存之所以要三级而不是两级
是因为我们的第三级缓存是一个objectfactory,工厂来创建bean。
如果你有两个对象都需要引用这个bean的话,如果没有二级缓存,那么三级缓存内部是会又重新创建了一个bean,
这显然是有问题的。
为什么要用ObjectFactory实例化Bean
是为了可以让我们的bean得到代理,实现功能的增强。