读完这篇文章你将会收获到web
Spring
中
prototype
类型的
bean
如何作循环依赖检测
Spring
中
singleton
类型的
bean
如何作循环依赖检测
继上一篇文章 Spring 获取单例流程(一) 咱们此次继续往下分析一下后面的流程spring
上一篇文章中咱们说到,首先咱们根据 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
建立的时候都会调用下面这个方法flex
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
完成以后ui
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 容器的初始化this
第二步则是判断当前的 beanFactory
是否有父容器(父 beanFactory
) ,若是有而且 beanName
的 beanDefinition
不存在当前的 beanFactory
中,那么则尝试在父容器中去获取这个 bean
url
咱们继续往下看下面的代码spa
// 若是不是仅仅作类型检查则是建立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