结合Spring Bean加载流程,本文对Spring单例构造器循环依赖及Field循环依赖进行分析。对于构造器循环依赖,目前Spring是没法解决的;Field循环依赖,Spring经过提早暴露实例化Bean及缓存不一样阶段的bean(三级缓存)进行依赖排除。网上也有很多一些关于这方面的文章,但做者想从缓存生命周期及多例Bean循环依赖这方面另辟蹊径,深刻理解下Spring Ioc的精髓。这是第二篇博文,但愿能养成梳理笔记的好习惯。web
循环依赖,简单地说,就是循环引用,两个或者多个 bean 相互之间的持有对方,造成一个闭环。如,A 依赖 B,B 又依赖 A,它们之间造成了循环依赖,又或者是 A 依赖 B,B 依赖 C,C 又依赖 A。能够用一张简图描述这种依赖关系。spring
Spring循环依赖的理论依据实际上是Java基于引用传递,当咱们获取到对象的引用时,对象的field或者或属性是能够延后设置的。接下来,将经过构造器循环依赖及Field循环依赖进行阐述。设计模式
在分析循环依赖以前咱们先回顾下Spring Bean加载的流程。缓存
1)项目启动时建立ServletContext实例,将context-param中键值对值存入ServletContext中;bash
2)当建立Context LoaderListener时,因为监听器实现了ServletContextListener接口,而ServletContextListener提供了监听web容器启动时,初始化ServletContext后的事件监听及销毁ServletContext前的事件监听;所以,contextLoaderListener默认实现contextInitialized和contextDestroyed这两个方法;容器的初始化就是从contextInitialized开始的;app
3)首先先建立WebApplicationContext的实例,若是配置了contextClass属性值,则表明配置了相应的WebApplicationContext容器实现类,若是没有配置,默认建立的实例对象是XmlWebApplicationContext;ide
4)经过contextConfigLocation获取容器加载的配置文件,循环遍历configLocation,调用AbstractBeanDefinitionReader的loadDefinitionBeans方法进行解析并注册,解析的过程主要有如下几个步骤:函数
将xml转换为Document对象,最终调用DefaultBeanDefinitionDocumentReader中的parseBeanDefinitions方法;post
解析Document中的Node节点,若是是默认的bean标签直接注册(调用的是org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinition方法,若是是自定义的命名空间标签,得到命名空间后,拿到对应的NamespaceHandler(从spring中的jar包中的meta-inf/spring.handlers属性文件中获取),调用其parse方法进行解析;ui
调用NamespaceHandler的init方法注册每一个标签对应的解析器;
根据标签名称得到对应的解析器,解析具体的标签; 解析注册这些步骤最终将解析所得的BeanDefinition放入一个map中,这时并无进行注入。
5)实例化 入口是AbstractApplicationContext#finishBeanFactoryInitialization方法,以getBean方法为入口,先从缓存中获取,若是拿不到时,经过工厂方法或构造器实例化一个Bean,对于构造器咱们能够指定构造参数。
6)依赖注入(populateBean) 装配bean依赖,项目中大都使用@Autowired注解,org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues,这个过程可能进行递归进行依赖注入;最终经过反射将字段设置到bean中。
7)初始化:经过后置处理器完成对bean的一些设置,如判断否实现intializingBean,若是实现调用afterPropertiesSet方法,建立代理对象等;
8)最终将根上下文设置到servletContext属性中;
Spring bean的加载最主要的过程集中在5,6,7这三个步骤中,对应着createBean、populateBean及intializeBean这三个方法上,循环依赖产生在createBean和populateBean这两个方法中。
对于构造器循环依赖,其依赖产生在实例化Bean上,也就是在createBean这个方法。对于这种循环依赖Spring是没有办法解决的。
<bean id = "aService" class="com.yfty.eagle.service.AService">
<constructor-arg index="0" ref="bService"/>
</bean>
<bean id = "bService" class="com.yfty.eagle.service.BService">
<constructor-arg index="0" ref="cService"/>
</bean>
<bean id = "cService" class="com.yfty.eagle.service.CService">
<constructor-arg index="0" ref="aService"/>
</bean>
复制代码
/**
* Callback before singleton creation.
* <p>The default implementation register the singleton as currently in creation.
* @param beanName the name of the singleton about to be created
* @see #isSingletonCurrentlyInCreation
*/
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
复制代码
在xml文件配置property属性或者使用@Autowired注解,其实这两种同属于一类。下面分析Spring是如何经过提早曝光机制+三级缓存来排除bean之间依赖的。
/** Cache of singleton objects: bean name --> bean instance */
一级缓存:维护着全部建立完成的Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** Cache of early singleton objects: bean name --> bean instance */
二级缓存:维护早期暴露的Bean(只进行了实例化,并未进行属性注入)
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
/** Cache of singleton factories: bean name --> ObjectFactory */
三级缓存:维护建立中Bean的ObjectFactory(解决循环依赖的关键)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
复制代码
由Spring Bean建立的过程,首先Spring会尝试从缓存中获取,这个缓存就是指singletonObjects,主要调用的方法是getSingleton;若是缓存中没有,则调下Spring bean建立过程当中,最重要的一个方法doCreateBean。
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从一级缓存中获取
Object singletonObject = this.singletonObjects.get(beanName);
// 若是一级缓存没有,而且bean在建立中,会从二级缓存中获取
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
// 二级缓存不存在,而且容许从singletonFactories中经过getObject拿到对象
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 != NULL_OBJECT ? singletonObject : null);
}
复制代码
分析: Spring首先从singletonObjects(一级缓存)中尝试获取,若是获取不到而且对象在建立中,则尝试从earlySingletonObjects(二级缓存)中获取,若是仍是获取不到而且容许从singletonFactories经过getObject获取,则经过三级缓存获取,即经过singletonFactory.getObject()。若是获取到了,将其存入二级缓存,并清除三级缓存。
若是缓存中没有bean对象,那么Spring会建立Bean对象,将实例化的bean提早曝光,而且加入缓存中。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
......
if (instanceWrapper == null) {
//这个是实例化Bean的方法,会调用构造方法,生成一个原始类型的Bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 提早曝光这个实例化的Bean,方便其余Bean使用
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
// 知足单例 + allowCircularReferences默认为true + bean在singletonsCurrentlyInCreation集合中时,earlySingletonExposure为true
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 将bean加入三级缓存中
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 属性注入,这里可能发生循环依赖
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
// 初始化bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
// 因为AService提早暴露,会走这段代码
if (earlySingletonExposure) {
// 从二级缓存中拿出AService(这个对象其实ObjectFactory.getObject()得来的,多是个包装类,
而exposedObject可能依然是实例化的那个bean,这时为保证最终BService中的AService属性与AService自己
持有的引用一直,故再次进行exposedObject的赋值操做,保证beanName对应实例惟一性。)
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
}
// ...........
return exposedObject;
}
复制代码
分析: 当经过无参构造,得到一个实例化bean时,Spring会将其提早曝光,即在实例化后注入属性前将其加入三级缓存中。下面以AService和BService相互依赖为例,说明依赖排除过程。
从上述分析可知,singletonFactories即三级缓存才是解决循环依赖的关键,它是一个桥梁。当AService初始化后,会从二级缓存中获取提早暴露的对象,而且赋值给exposedObject。这主要是二级缓存的对象earlySingletonReference多是包装类,BService持有的引用就是这个earlySingletonReference,赋值后保证beanName对应实例惟一性,这点耐人寻味。
咱们已经知道了Spring如何解决循环依赖了,可是对于Spring为何这么设计,总感受云里雾里,网上的博文大都没有讲这点。下面做者谈下本身的观点。
三级缓存采用工厂设计模式,经过getObject方法获取bean,就循环依赖而言,当BService经过populateBean注入AService时,要保证BService中的AService内存地址a1和AService最终初始化后的地址a2一致,而此时AService才刚刚实例化,a1与a2不必定相等,经过三级缓存能够获取到最终引用地址,这保证了在循环依赖能获取到真正的依赖。
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
复制代码
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
if (exposedObject == null) {
return null;
}
}
}
}
return exposedObject;
}
复制代码
实例化的bean是什么时候加入缓存中,又是什么时候将其删除的,它们之间有什么区别呢?接下来,本文会一一做答。
当earlySingletonExposure属性为true时,将beanFactory加入缓存;当经过getSingleton从三级缓存中取出实例化的原始bean时或者完成初始化后,并清除singletonFactories中bean的缓存。
当earlySingletonExposure属性为true时,将beanFactory加入缓存,当经过getSingleton从三级缓存中取出实例化的原始bean时,此时,将获取的bean加入二级缓存。当完成bean初始化,将bean加入一级缓存后,清除二级缓存;
当完成bean初始化,经过addSingleton将bean加入一级缓存singletonObjects中,而且这个缓存是常驻内存中的。
从上述分析可知,三级缓存和二级缓存是不共存的,且其在Spring完成初始化,都会被清除掉。
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
复制代码