@java
关于Spring 循环引用 网上的分析文章不少,写的水平参差不齐,虽然看完了 知道怎么个回事 可是过段时间仍是忘记了,主要本人没过目不忘的本领哈,可是只要记住主要的点就行了spring
可是若是你本身想更深刻的了解,仍是要本身去看源码分析一波,由于别人分析的时候,有些知识点你是get不到的,只有当本身走进源码去看的时候,才有get到更多的!好比网上不少文章都分析Springs是怎么解决循环依赖的 可是为何只有单类的才能够,Prototype的就不行呢,在哪里不行,或者说构造器的注入为何也不能够,最后若是解决循环依赖,或者说 怎么去换中写法去解决问题。缓存
纸上得来终觉浅 绝知此事要躬行! 这句话献给正在读文章的你,看完记得点赞,还有就是本身去下载Spring 源码 去看看app
OK,进入正文,固然上面也不是废话啦,Spring 的循环引用 我想读者们应该知道,不知道的话,算了 来个code把!ide
@Component public class CycleTestServiceA { private CycleTestServiceB b; public void setB(CycleTestServiceB b) { this.b = b; } } @Component public class CycleTestServiceB { private CycleTestServiceA a; public void setA(CycleTestServiceA a) { this.a = a; } }
上面的 代码 就是一个普通的set注入的方式,A里面依赖B,B里面依赖A,这样就致使了循环依赖,Component默认是Singleton的函数
咱们从Spring Beanc建立开始做为入口,在Spring IoC 容器中一个完整的Bean 要进过实例化 和初始化的阶段源码分析
Spring Bean 实例化就getBean的过程ui
那咱们接进入源码去看下getBean的过程this
getBean方法时 BeanFactory 接口的方法 他的实现类有不少,咱们跟进去他的抽象实现类org/springframework/beans/factory/support/AbstractBeanFactory.java 类,其实都是调用了doGetBean方法prototype
下面是我截取的核心代码
protected <T> T doGetBean( final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); Object bean; /* * 检测是否 有缓存对象 这个方法时处理循环依赖的关键入口 * 记住这个的代码 我还会回来的 * */ Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { if (logger.isDebugEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.debug("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { logger.debug("Returning cached instance of singleton bean '" + beanName + "'"); } } bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { /* *Prototype bean 是否在建立当中 若是存在 说明产生了循环依赖 处理Bean 循环依赖的地方 *这个地方就是为何Scope 是Prototype的时候 会报循环依赖的错误,慢慢看 后面会解释 * */ if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } ... if (!typeCheckOnly) { markBeanAsCreated(beanName);//这个方法就是把当前的bean 加入到alreadyCreated的set集合中 后面有些判断须要 } try { ... /* * 获取Bean 的依赖项 这边的依赖 是咱们在xml 有时候能够配置的depends-on的依赖 和咱们本次讲的循环依赖不是同一个 * 我特别说明下 * */ String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { //注册依赖 建立Bean 等 } } /* * 若是是单列 建立createBean 记住这个的代码 我还会回来的 * */ if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { ... } } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } /* * Prototype对象 * */ else if (mbd.isPrototype()) { // It's a prototype -> create a new instance. Object prototypeInstance = null; try { beforePrototypeCreation(beanName); prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } /* * 不是Singleton也不是Prototype,多是自定义scope的对象 * */ else { ... } } } ... return (T) bean; }
上面是dogetBean()的核心方法
带着这个问题 咱们能够从上面的代码中 看下 Spring在处理么Prototype的时候 有2个方法beforePrototypeCreation(),afterPrototypeCreation(),
上下代码
/** Names of beans that are currently in creation */ private final ThreadLocal<Object> prototypesCurrentlyInCreation = new NamedThreadLocal<Object>("Prototype beans currently in creation"); protected void beforePrototypeCreation(String beanName) { Object curVal = this.prototypesCurrentlyInCreation.get(); if (curVal == null) { this.prototypesCurrentlyInCreation.set(beanName); } else if (curVal instanceof String) { Set<String> beanNameSet = new HashSet<String>(2); beanNameSet.add((String) curVal); beanNameSet.add(beanName); this.prototypesCurrentlyInCreation.set(beanNameSet); } else { Set<String> beanNameSet = (Set<String>) curVal; beanNameSet.add(beanName); } } protected void afterPrototypeCreation(String beanName) { Object curVal = this.prototypesCurrentlyInCreation.get(); if (curVal instanceof String) { this.prototypesCurrentlyInCreation.remove(); } else if (curVal instanceof Set) { Set<String> beanNameSet = (Set<String>) curVal; beanNameSet.remove(beanName); if (beanNameSet.isEmpty()) { this.prototypesCurrentlyInCreation.remove(); } } }
上面的代码 我相信小伙伴都能看的懂,就是用一个set集合存储当前正在建立的Bean的BeanName,并且是用ThreadLocal去存储Set集合的 ThreadLocal是每一个线程私有的。看到这个 咱们再把目光往代码上面看一看 isPrototypeCurrentlyInCreation这个方法的判断
protected boolean isPrototypeCurrentlyInCreation(String beanName) { Object curVal = this.prototypesCurrentlyInCreation.get(); return (curVal != null && (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName)))); }
看到了么 这边就是用这个ThreadLocal里面的set集合去判断的,为何用ThreadLocal想下,你想呀,A依赖B,而B依赖A,AB都是Prototype的,A建立的时候 A会加入到这个set集合中,而后A去填充实例的时候,由于要依赖B,因此去getB,发现B又依赖A,这个时候有要getA,你看 当执行到 最上面的判断isPrototypeCurrentlyInCreation的时候,是不报了循环引用的错,由于A已经在prototypesCurrentlyInCreation的Set集合中了,由于整个流程必定是一个线程走下去的,因此存入ThreadLocal中,一点问题没有,并且还不受其余线程影响~
不论是哪一种Scope 都是要调用createBean方法的,咱们跟进去代码 发现惟一重写的实如今org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java 中
咱们进入代码看下
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException { RootBeanDefinition mbdToUse = mbd; ... try { // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. // 该函数的做用是给 BeanPostProcessors 后置处理器返回一个代理对象的机会 // 这里是实现AOP处理的重要地方 // AOP是经过BeanPostProcessor机制实现的,而接口InstantiationAwareBeanPostProcessor是实现代理的重点 Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } ... /* * 后置处理器 没有返回有效的bean 就建立 * */ Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isDebugEnabled()) { logger.debug("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; }
这边 我看到一句英文注释,都没舍得替换中文,Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. 哈哈 给后置处理器一个返回代理bean的机会,这边就是Spring 中实现AOP的重点,动态代理 其实就是使用后置处理器 替换了target Bean 的实例,从而达到代理的做用,这个之后聊到AOP 的时候在慢慢聊吧!这个最核心的代码还在再doCreateBean中,继续跟进
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException { // Instantiate the bean. BeanWrapper instanceWrapper = null;//BeanWrapper 是Bean 的包装类 方便对Bean 实例的操做 if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); mbd.resolvedTargetType = beanType; .... boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));//知足三个条件 单列 运行循环引用 bean 是否正在建立中 if (earlySingletonExposure) { addSingletonFactory(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean);//提早暴露引用 获取早期的引用 } }); } // Initialize the bean instance. 初始化Bean 实例 Object exposedObject = bean; try { populateBean(beanName, mbd, instanceWrapper);//填充Bean if (exposedObject != null) { exposedObject = initializeBean(beanName, exposedObject, mbd);//执行初始化Bean里面的方法 } } ... if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { /* *这边其实仍是作了一个判断,exposedObject是通过了 initializeBean方法方法的 *而bean仍是那个提早暴露的Bean, *为何要作这个判断你,是由于exposedObject通过了initializeBean里面的后置处理器的修改 可能Object 已经改变了 **/ if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } /* *有兴趣的能够根据到上面的每个方法看下 ,这边就是判断若是提早暴露的bean已经和在后置处理器里面修改了而且不同了,就抛出异常,由于提早暴露的Bean 可能做为了另外的bean的依赖 这样就会致使单类的bean在容器中有2个实例的出现,这是非法的! */ if (!actualDependentBeans.isEmpty()) { //抛出一个异常 因为不少文字我就删掉了 } } } } ... return exposedObject; }
earlySingletonExposure这个主要关注的是earlySingletonExposure 这边的代码,这个就是在Bean 实例化完成后,开始填充属性之间发的代码
earlySingletonExposure为true 要知足三个条件
前面2个能够理解 那最后一个又是什么呢?话很少说 进入方法看下
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16)); public boolean isSingletonCurrentlyInCreation(String beanName) { return this.singletonsCurrentlyInCreation.contains(beanName); }
这个方法 一看很简答 就是判断当前的bean是否在singletonsCurrentlyInCreation的set集合中 那这个集合又是何时加入的呢?带着这个想法 我又重头扫描了一篇代码 还记的org/springframework/beans/factory/support/AbstractBeanFactory.java代码中的doGetBean()方法里面Singleton的Bean 在建立Instance的时候是调用了getSingleton方法么,不清楚的话 能够往上看下
这个方法 是 addSingletonFactory 方法 构建ObjectFactory的参数的时候 里面返回使用方法
看下代码:
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; }
里面最主要的就是看下getEarlyBeanReference方法 这个方法时SmartInstantiationAwareBeanPostProcessor里面的方法,他的实现有2个 一个是org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessorAdapter.java 还有一个是动态代理使用的 我就不列举了,
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { return bean; }
看了下 其实InstantiationAwareBeanPostProcessorAdapter的重写就是 返回了当前的bean 没有作任何操做。这边其实就是作了一个引用的保存。
代码位于org/springframework/beans/factory/support/DefaultListableBeanFactory.java中
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { synchronized (this.singletonObjects) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { ... beforeSingletonCreation(beanName); boolean newSingleton = false; ... try { singletonObject = singletonFactory.getObject(); newSingleton = true; } finally { afterSingletonCreation(beanName); } if (newSingleton) { addSingleton(beanName, singletonObject); } } return (singletonObject != NULL_OBJECT ? singletonObject : null); } }
这个方法其所就是SingletonBean的核心建立流程
protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } }
当我看到这个singletonsCurrentlyInCreation.add的时候 我很欣慰 由于我以前的问题解决了 就是这个方法把Bean 放入到以前的singletonsCurrentlyInCreation的集合中的
这个应该都很清楚了 就是咱们方法传入的匿名的ObjectFactory对象,当以前getObject的时候 才会执行咱们刚才的看的createBean方法
protected void afterSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) { throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation"); } }
看下afterSingletonCreation方法里面的东西也很简单,就是从singletonsCurrentlyInCreation集合中移除
先看下代码
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); } }
看到 这边 就不得表介绍下三级缓存了
说道这里 咱们就清楚了 这个方法其所就是在二级缓存和三级缓存中删除当前的Bean,把当前的Bean 放入到一级缓存中,由于到了这一步 bean 的实例化,属性填充,后置处理器执行,初始化等方法都已经执行了。
这个方法 哪里用的呢 那咱们有要回到上面的代码doCreateBean中 当earlySingletonExposure为true的时候 会调用这个方法addSingletonFactory
这个方法就是 当前的Bean能够提早引用的话执行的方法
看下代码也很简答
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); } } }
看下这个方法 说白了就是往三级缓存里面存放bean的ObjectFactory对象 这个地方也是处理循环引用的关键,这个时候Bean 刚刚进行了实例化 尚未进行bean的属性填充和初始化等一些列方法
那怎么去解决提早引用的呢?能够看下ObjectFactory返回的是getEarlyBeanReference对象
这个方法是在doGetBean方法中 从缓存中获取Bean 对象的方法 这个方法很关键 是处理循环依赖的入口,那咱们跟进去看下方法
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); }
最终调用的方法如上,allowEarlyReference是为true的,咱们仍是用最上面的ServiceA和ServiceB 为例,第一次ServiceA 进入的时候 是无法进入下面的判断的 应为当前ServiceA不在SingletonCurrentlyInCreation中,可是当第二次进来,第二次是何时呢,就是在填充ServiceB的时候 须要依赖 ServiceB,这个时候ServiceB也要执行getBean的流程,发现又依赖ServiceA,这个时候 ServiceA就是在SingletonCurrentlyInCreation的集合中了,并且在三级缓存中,这个时候会进行判断条件里面的方法,先找一级缓存,找不到就找二级缓存,最后找三级缓存,而后将取出三级缓存里面的ObjectFactory执行getObject方法 就是获取咱们上面提到的提早引用的bean,最后将bean 放入到二级缓存,从三级缓存中移除~
看完了 上面的一推 也许很懵逼,可能也是我文字组织能力差,只能之后慢慢改变
上面涉及到几个缓存 我在边在重写描述一下
名称 | 类型 | 使用说明 | 所属类 |
---|---|---|---|
singletonObjects | Map<String, Object> | 实例化 初始化都完成的bean的缓存 | DefaultSingletonBeanRegistry.java |
earlySingletonObjects | Map<String, Object> | 可提早引用的 Bean 缓存,这里面的Bean 是一个非完整的bean,属性填充 后置处理器都未执行的bean | DefaultSingletonBeanRegistry.java |
singletonFactories | Map<String, ObjectFactory<?>> | 单类bean的建立工厂函数对象 | DefaultSingletonBeanRegistry.java |
singletonsCurrentlyInCreation | Set
|
立刻要建立的单类Bean的集合 | DefaultSingletonBeanRegistry.java |
prototypesCurrentlyInCreation | ThreadLocal | object 是一个Set
|
AbstractBeanFactory.java |
alreadyCreated | Set
|
至少建立过一次的Bean 在提早暴露的bean修改了致使不一致时 判断会用到 | AbstractBeanFactory.java |
最终我仍是用一个方法执行的流程图 来描述下 循环依赖的处理
那么为何构造器的注入方式不行呢?缘由是由于 Bean在实例化阶段的时候createBeanInstance的时候就会去建立依赖的B,这样的话A根本就走不到提早暴露的代码块,因此会报一个循环引用的错误,报错的地方就是构造函数参数bean 建立的地方,本身能够写个demo,调试下 在哪一步报错,博主但是看了半天 才找到,哈哈!
关于若是解决构造器的循环注入
https://www.baeldung.com/circular-dependencies-in-spring
这是一篇外国博文,小伙伴们能够看下
Spring 处理循环依赖的核心就是 三级缓存,让Bean 提早暴露出来,能够提早引用,让互相依赖的Bean 能够流程上执行下去,从而解决了循环依赖的问题
最后的最后 仍是本身对照源码 本身理解一遍,我相信必定会加深你的理解,必定会有收获
码字不易,花了一个周末的时间,各位看官喜欢的话点个赞,鼓励下博主,继续创造,多谢~