目录
1. 前言
在博客《spring系列3-IOC的实现》的第3节通过两张流程图大致梳理了IOC主要逻辑,值得关注的是3处红色标注的各级缓存放入处。
考虑一下常见问题,加深对IOC三级缓存的理解:
问题1:IOC是如何解决循环依赖的?
比较浅地回答:通过缓存。在对象已实例化,但属性未装配(populate)前,将早期bean放入缓存中,使得递归解决属性注入时,依赖早期bean的其它bean能够拿到早期bean的引用。
问题2:IOC解决循环依赖为什么需要3级缓存,只用1级缓存行不行?只用2级缓存行不行?
根据博客:https://my.oschina.net/u/4340310/blog/4332450
(1)1级缓存不够,原因:如果只有1级缓存,存放了已实例化但未装配属性的bean,值=null的属性可能引发空指针,所以至少需要2级,一级存放完全可用的bean、二级存放早期的bean。
(2)2级缓存不够,参考别人的博文没想明白,只知道与代理、SmartInstantiationAwareBeanPostProcessor接口有关。还是自己动手写用例,通过debug来理解。
2. 如何写demo
研究SmartInstantiationAwareBeanPostProcessor接口
《spring系列3-IOC的实现》第3节的第2张流程图的三级缓存放入处代码如下:
在《spring系列3-IOC的实现》第3节的第1张流程图的二级缓存放入处回调getEarlyBeanReference(...)方法,方法内部会调用SmartInstantiationAwareBeanPostProcessor接口的getEarlyBeanReference(...)方法,而上述接口的直接实现类只有2个:AbstractAutoProxyCreator(可能返回代理)、InstantiationAwareBeanPostProcessorAdapter(直接返回bean)。
既然要理解第3级缓存对于代理情况的作用,那就基于AbstractAutoProxyCreator写用例。
ps:如果对advice陌生,可以了解一下《spring系列4-AOP的实现》
3. demo
demo很简单,就是Mother类、Child类相互依赖:
//Child类依赖Mother类
public class Child {
private Mother mother;
public Mother getMother() {
return mother;
}
public void setMother(Mother mother) {
this.mother = mother;
}
}
//Mother类依赖Child类
public class Mother {
private Child child;
public Child getChild() {
return child;
}
public void setChild(Child child) {
this.child = child;
}
}
//起床BPP处理器,只对Mother类返回advice
public class GetUpPostProcessor extends AbstractAutoProxyCreator {
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource customTargetSource) throws BeansException {
if (beanClass.equals(Mother.class)) {
return new Object[]{new GetUpAdvice()};
} else {
return null;
}
}
static class GetUpAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("Hurry up! You will be late!");
}
}
}
public class MainTest {
@Test
public void test() {
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("mytest/ioc/circularrefercence/beanFactoryTest.xml"));
BeanPostProcessor postProcessor = new GetUpPostProcessor();
((XmlBeanFactory) bf).addBeanPostProcessor(postProcessor);
Mother mother = (Mother) bf.getBean("mother");
System.out.println(mother);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
<bean id="mother" class="com.mytest.ioc.circularreference.Mother">
<property name="child" ref="child"/>
</bean>
<bean id="child" class="com.mytest.ioc.circularreference.Child">
<property name="mother" ref="mother"/>
</bean>
</beans>
4. 分析demo
获取bean的流程如下:
继续回答问题2(2):2级缓存不够,原因:2th级缓存用于存放proxy或未注入属性的bean,3th缓存用于存放生产2th缓存的工厂。
是否可以将3th缓存生产的内容直接放入2th缓存,不用3th缓存记录工厂呢?我想应该是不行的,工厂生产的proxy无法装配属性,调用populateBean方法会错误吧。