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
此次必定?
此次必定?
相關文章
相關標籤/搜索