前言 :
Spring在进行属性注入时:
注入普通属性时 : String int等存储基本类型的集合时,直接通过set方法的反射设置进去
注入单向对象引用属性时 : 从当前容器中getBean 获取后通过set方法反射设置进去,如果当前容器没有, 则会先创建被注入对象bean实例(完成整个生命周期)后,在注入进当前bean。
注入双向对象引用属性时 : 可以说循环依赖 涉及到三级缓存
三级缓存:
通过ClassPathxmlApplicationContext容器类中太祖宗类"DefaultSingletonBeanRegistry"中我们发现
singletonObjects -- 单例池 一级缓存 存储完整Bean
earlysingletonObjects -- 早期Bean单例池 二级缓存 存储被引用的半成品Bean
singletonFactories -- 单例Bean工厂池 三级缓存 存储未被引用的半成品Bean
这便是spring中的三级缓存
应用场景:
我们假设要往Spring容器中注入两个bean
<bean id="mapper" class="xxx">
<property name="service" ref="service"></property>
</bean>
<bean id="service" class="xxx">
<property name="mapper" ref="mapper"></property>
</bean>
在创建mapper需要注入service 创建service需要注入mapper 在没有三级缓存的情况下为
这就是通常我们说的循环依赖会直接造成死循环
那么Spring是如何通过三级缓存解决这个问题的呢 现在就来我们来揭晓 , 通过翻阅源码我在这里简易的为大家解答
还是针对于上方案例 :
假设先执行service的创建, 在service实体化为对象时 在堆内存中会开辟一个空间存储
这个时的service还未被初始化 还只是一个半成品的bean 存入singletonFactories中
执行属性注入时需要一个mapper 这时的容器中还没有mapper
mapper实例化对象 加入singletonFactories中
singletonFactories中此时存储了 mapper 与 service
mapper属性注入 在三级缓存中获取到service, service从三级缓存移到二级缓存
mapper执行完bean的生命周期成为一个完整的bean存储到单例池中,删除二三级缓存
回溯到service的属性注入 此时容器中已经有了mapper,直接注入 , 然后执行完生命周期成为完整bean存入单例池,删除二三级缓存。
由于源码的翻阅过程特别繁琐 需要特别长的时间 这里使用文字简洁描述一下如果工作的
写一段测试代码试试把...
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Bean的生命周期/Beanfactory.xml");
Object bean1 = applicationContext.getBean("service");
Object bean2 = applicationContext.getBean("mapper");
System.out.println(bean1);
System.out.println(bean2);
}
控制台输出结果: