spring的三级缓存

时间:2021-08-01 01:17:40

问题引入

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得到代理,实现功能的增强。