读完这篇文章你将会收获到
Spring
中prototype
类型的bean
如何做循环依赖检测Spring
中singleton
类型的bean
如何做循环依赖检测
前言
继上一篇文章 Spring 获取单例流程(一) 我们这次继续往下分析一下后面的流程
image-20200530152154079
上一篇文章中我们说到,首先我们根据 name
找到其对应的 beanName
、然后去缓存中看是否已经创建了/创建中这个对应的 bean
,如果在缓存中找到了这个 bean
、那么我们需要对这个 bean
可能进行一些处理,比如说用户想要的是一个普通的 bean
、但是在 Spring
缓存中找到的是一个 factoryBean
、这个时候就要调用 fatoryBean
的 getObject
方法以及对应的一些前置方法以及回调等。
那么如果我们在缓存中找不到这个 bean
那么流程又是怎么样?这个就是这篇文章要跟大家一起分享的
源码分析
if (sharedInstance != null && args == null) { // 这里被我删除了一些spring 的log // 处理一下 factory bean 的情况、包括从 factory beans 的缓存中获取、或者重新调用 factory bean 的 get bean 方法 包括一些回调 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // 从 上面的 getSingleton 拿不到对象的bean 、说明这个bean的scope 要么不是 singleton 要这个bean是singleton 但是没有初始化一句 // 因为 Spring 只解决单例模式下得循环依赖,在原型模式下如果存在循环依赖则会抛出异常 // 这里的循环依赖检查使用的 是 threadLocal 因为 prototype 类型的只是 if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // 如果容器中没有找到,则从父类容器中加载 BeanFactory parentBeanFactory = getParentBeanFactory(); // parentBeanFactory 不为空且 beanDefinitionMap 中不存该 name 的 BeanDefinition if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // Not found -> check parent. // 这里只是找出他的真正的beanName、并没有去掉 factory bean 的前缀 String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean( nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { // Delegation to parent with explicit args. return (T) parentBeanFactory.getBean(nameToLookup, args); } else if (requiredType != null) { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } else { return (T) parentBeanFactory.getBean(nameToLookup); } } ....... ....... ........ } 复制代码
第一步就是判断这个是否是一个 prototype
类型的 bean
,如果是并且正在创建中、那么就抛出一个循环依赖的异常
protected boolean isPrototypeCurrentlyInCreation(String beanName) { // prototypesCurrentlyInCreation 是一个 threadLocal Object curVal = this.prototypesCurrentlyInCreation.get(); return (curVal != null && (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName)))); } 复制代码
每一个 prototype
的 bean
创建的时候都会调用下面这个方法
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<>(2); beanNameSet.add((String) curVal); beanNameSet.add(beanName); this.prototypesCurrentlyInCreation.set(beanNameSet); } else { Set<String> beanNameSet = (Set<String>) curVal; beanNameSet.add(beanName); } } 复制代码
curVal
要么是一个 String
要么是一个 Set
, 而在创建 prototype bean
完成之后
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(); } } } 复制代码
可以看到 Spring
使用 ThreadLocal
去做一个循环依赖的检测、我们在 Spring
资源加载的源码分析里面也提及到了、也是使用 ThreadLocal
进行一个资源的循环引用的检测 Spring 容器的初始化
第二步则是判断当前的 beanFactory
是否有父容器(父 beanFactory
) ,如果有并且 beanName
的 beanDefinition
不存在当前的 beanFactory
中,那么则尝试在父容器中去获取这个 bean
我们继续往下看下面的代码
// 如果不是仅仅做类型检查则是创建bean,标记这个bean 已经创建了或者将要被创建 if (!typeCheckOnly) { markBeanAsCreated(beanName); } try { // 从容器中获取 beanName 相应的 GenericBeanDefinition,并将其转换为 RootBeanDefinition final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); // 检查给定的合并的 BeanDefinition checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on. // 处理所依赖的 bean String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { // 如果是循环依赖 if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } // 注册 registerDependentBean(dep, beanName); try { // 看看我依赖的大佬好了没 getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } ...... ...... 复制代码
第三步则是从 BeanDefinitionRegistry
中获取注册的 BeanDefinition
继而获取这个 bean
所要依赖的其他 bean
,遍历其所依赖的 bean
、判断是否循环依赖了
protected boolean isDependent(String beanName, String dependentBeanName) { synchronized (this.dependentBeanMap) { return isDependent(beanName, dependentBeanName, null); } } private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) { if (alreadySeen != null && alreadySeen.contains(beanName)) { return false; } String canonicalName = canonicalName(beanName); // 找出依赖这个beanName的集合 Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName); // 没有人依赖这个beanName if (dependentBeans == null) { return false; } // 哦嚯、beanName 依赖的 bean、也依赖着beanName、完蛋 if (dependentBeans.contains(dependentBeanName)) { return true; } // 看看依赖 beanName 的 其他依赖、有没有被dependentBeanName 依赖 // A 想依赖F、BCDE 依赖着A、那么我们现在来到这一步、已经确定了F不依赖A、那么我们要看看F是否依赖BCDE、如果依赖、那么就是循环依赖了 for (String transitiveDependency : dependentBeans) { if (alreadySeen == null) { alreadySeen = new HashSet<>(); } alreadySeen.add(beanName); if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) { return true; } } return false; } 复制代码
每一个 bean
创建之前都会注册其依赖关系、主要由两个 Map
组成、一个是 key
为被依赖者,value
为依赖者集合,另一个则是 key
为依赖者,value
为被依赖者集合,比如说 beanA
依赖着 beanB
和 beanC
key 为被依赖者 value 为依赖者集合 beanB ---> beanA beanC ---> beanA key 为依赖者,value 为被依赖者集合 beanA ---> beanB,beanC 复制代码
第四步则是去注册依赖关系,也就是往上面的两个 Map
中存放数据
public void registerDependentBean(String beanName, String dependentBeanName) { String canonicalName = canonicalName(beanName); // 在这个里面加上 这个依赖我的人 synchronized (this.dependentBeanMap) { Set<String> dependentBeans = this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8)); if (!dependentBeans.add(dependentBeanName)) { return; } } // 在这里将我依赖的 那个大佬放进去我依赖的列表中 synchronized (this.dependenciesForBeanMap) { Set<String> dependenciesForBean = this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8)); dependenciesForBean.add(canonicalName); } } 复制代码
最后的 getBean
则回到我们最初的起点
getBean(dep); 复制代码
今天我们就先分析到这里、后续的话我们在后面的文章继续探讨。今天我们大致分析了
总结
- 根据参数中的
name
找出对应的beanName
、无论这个name
是别名或者是一个factoryBean
的beanName
- 查看缓存中是否包含这个
beanName
对象
- 先从一级缓存
singletonObjects
中看看有没有 - 然后从二级缓存
earlySingletonObjects
- 都没有的话再从三级缓存
singletonFactories
中看看有没有
- 如果缓存中有
bean
、那么我们还是需要处理一下这个bean
- 如果
Spring
缓存中返回的bean
是factoryBean
、而用户也想要的是一个beanFactory
(参数name
中的前缀是&
)、那么我们直接返回 - 如果
Spring
缓存中返回的bean
是普通的bean
、而用户也想要的是一个普通的bean
、那么就直接返回 - 如果
Spring
缓存中返回的bean
是一个factoryBean
、而用户想要的是一个普通的bean
、那么我们就要从factoryBean
中获取这个bean
- 而从
factoryBean
中获取这个bean
的过程中、需要调用到前置处理、后置处理和我们常用的接口回调BeanPostProcessor
- 如果缓存中没有
bean
、则判断是否是prototype
类型并且循环依赖 - 如果没有则尝试能否在父容器中找到该
bean
- 如果父容器也没有则获取该
beanName
对应的beanDefinition
找出其依赖的beanName
- 判断该
beanName
与 依赖的beanName
是否循环依赖、没有则注册其依赖关系并调用getBean
方法去创建依赖的beanName