spring依赖注入——循环依赖
上一篇博客简单地分析了下依赖注入。但是对于依赖注入的很多细节,都没有深入的分析。这一篇博客会继续分析spring的依赖注入。这篇博客会处理分析getBean缓存时候遗留下来的循环依赖问题。
循环依赖分析
首先明确下,只有单例情况下,spring才会试着去处理循环依赖问题,多例是不会去处理循环依赖的。这个也好了解,假如是多例的话,比方a -> b 并且 b -> a 那么,当A a=new A(); 之后要注入b,b却是多例的,那么到底该注入哪个B是不确定的。如下图:

接下来我们分析,为啥会有循环依赖的问题。
先来分析没有循环依赖的问题
public static class A{ private B b; //省略get和set方法}public static class B{}这个时候,假如spring先初始化A,而后会发现A依赖于B,而后就会初始化B,最后注入到A里。
整个流程用代码表示大概如下所示:
A a = 创立AB b = 创立B -----> 创立A子流程 a.setB(b);但是假设B也依赖了A呢?即
public static class B{ private A a;}那样依赖注入过程就会变成(简单示例)
A a = 创立AB b = 创立B ----> 创立B子流程 b.setA(????); // 这个时候A还没创立完成呢a.setB(b);那么如何处理呢?很简单,把那个还没创立完的A(只是new了,但是没有进行依赖注入的A)set到B里就好了。

弄清了这个流程之后,我们再来分析spring是如何进行依赖注入的。
spring对引用类型的注入
这里我先从spring对引用属性的注入开始。
<bean id="person" class="com.hdj.learn.spring.demo.Person"> <property name="car" ref="car"></property></bean>即ref的注入
private Object resolveReference(Object argName, RuntimeBeanReference ref) { try { String refName = ref.getBeanName(); refName = String.valueOf(doEvaluate(refName)); if (ref.isToParent()) { if (this.beanFactory.getParentBeanFactory() == null) { throw new BeanCreationException( this.beanDefinition.getResourceDescription(), this.beanName, "Can't resolve reference to bean '" + refName + "' in parent factory: no parent factory available"); } return this.beanFactory.getParentBeanFactory().getBean(refName); } else { Object bean = this.beanFactory.getBean(refName); this.beanFactory.registerDependentBean(refName, this.beanName); return bean; } } catch (BeansException ex) { throw new BeanCreationException( this.beanDefinition.getResourceDescription(), this.beanName, "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex); }}实现很简单,就是从beanFactory里获取要依赖的对象
我们再来回顾下流程

问题是,当走到6时候,似乎又会回到1,这样不就死循环了么?
重点就是,第六步的获取A,我们回到doGetBean方法中。
getSingleton 方法
protected Object getSingleton(String beanName, boolean allowEarlyReference) { //已创立的对象里面找下 Object singletonObject = this.singletonObjects.get(beanName); //没找到,并且当前类正在被创立 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); 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);}我们在分析初始化bean的缓存部分时,曾分析过这几个缓存。当时其实只知道了singletonObjects是存储了已经创立了的对象。
现在让我们回头再看看这些缓存。
首先看看singletonFactories赋值的地方。
doCreateBean
// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { //默认实现返回bean return getEarlyBeanReference(beanName, mbd, bean); } });}在创立bean时候,有这样一段代码,当需要进行提前暴露时候(当前创立对象单例 + 允许循环引用 + 当前类正在被创立)会调用addSingletonFactory方法
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }}这里就碰见了我们要分析的singletonFactories,它存储了beanName -> 此处的匿名内部类singletonFactory。
singletonFactory
我们再回到getSingleton方法,以及之前绘制的图
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName);}逻辑很简单

三级缓存
我们经常听说spring通过三级缓存处理了循环依赖,其实三级缓存非常简单。就是指我们分析过的
Map<String, Object> singletonObjects
Map<String, ObjectFactory<?>> singletonFactories
Map<String, Object> earlySingletonObjects
这里分别举这样三个例子。
A 依赖 B(B不依赖A)
一级缓存
A 依赖 B && B依赖A
三级缓存
A依赖B && B依赖A + B依赖C && C 依赖 A
二级缓存
至此所谓的三级缓存及其使用,应该就非常的清楚了。
总结下
这篇博客分析了spring依赖注入中,循环依赖的问题,分析了在spring初始化bean时,各级缓存的作用。应该算是挺清晰的了。
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » spring依赖注入——循环依赖