Spring 获取单例流程(二)

读完这篇文章你将会收获到web

  • Springprototype 类型的 bean 如何作循环依赖检测
  • Springsingleton 类型的 bean 如何作循环依赖检测

前言

继上一篇文章 Spring 获取单例流程(一) 咱们此次继续往下分析一下后面的流程spring

image-20200530152154079
image-20200530152154079

上一篇文章中咱们说到,首先咱们根据 name 找到其对应的 beanName 、而后去缓存中看是否已经建立了/建立中这个对应的 bean,若是在缓存中找到了这个 bean、那么咱们须要对这个 bean 可能进行一些处理,好比说用户想要的是一个普通的 bean 、可是在 Spring 缓存中找到的是一个 factoryBean、这个时候就要调用 fatoryBeangetObject 方法以及对应的一些前置方法以及回调等。缓存

那么若是咱们在缓存中找不到这个 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)))); } 复制代码

每个 prototypebean 建立的时候都会调用下面这个方法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) ,若是有而且 beanNamebeanDefinition 不存在当前的 beanFactory 中,那么则尝试在父容器中去获取这个 beanurl

咱们继续往下看下面的代码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 依赖着 beanBbeanC

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 是别名或者是一个 factoryBeanbeanName
  • 查看缓存中是否包含这个 beanName 对象
    • 先从一级缓存 singletonObjects 中看看有没有
    • 而后从二级缓存 earlySingletonObjects
    • 都没有的话再从三级缓存 singletonFactories 中看看有没有
  • 若是缓存中有 bean、那么咱们仍是须要处理一下这个 bean
    • 若是 Spring 缓存中返回的 beanfactoryBean 、而用户也想要的是一个 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
此次必定?
此次必定?
相关文章
相关标签/搜索