Spring 獲取單例流程(一)

讀完這篇文章你將會收穫到web

  • getBean 方法中, Spring 處理別名以及 factoryBeanname
  • Spring 如何從多級緩存中根據 beanName 獲取 bean
  • Spring 如何處理用戶獲取普通 beanfactoryBean

引言

Spring 容器的初始化 中,咱們瞭解到 Spring 是如何將 XML 文件轉換爲 BeanDefinition 並註冊到 BeanDefinitionRegstryspring

今天咱們一塊兒繼續學習 Springbean 加載緩存

public static void main(String[] args) {
 Resource resource = new ClassPathResource("coderLi.xml");  DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();  XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);  xmlBeanDefinitionReader.loadBeanDefinitions(resource);  } 複製代碼
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "https://www.springframework.org/dtd/spring-beans-2.0.dtd"> <beans>  <bean class="com.demo.data.Person">  <description>  微信搜一搜:CoderLi  </description>  </bean> </beans> 複製代碼

熟悉的味道、熟悉的配方微信

源碼分析

咱們能夠在上面的 Java 代碼中加入如下的代碼併發

System.out.println(defaultListableBeanFactory.getBean("com.demo.data.Person#0"));
複製代碼

咱們根據默認的 beanName 從 Spring 容器中獲取 Person 這個 bean 對象,固然咱們可使用默認的別名獲取app

System.out.println(defaultListableBeanFactory.getBean("com.demo.data.Person"));
複製代碼

對 Spring 別名不熟悉的朋友能夠先看下個人這一篇文章 Spring-AliasRegistry編輯器

咱們直接進入到 AbstractBeanFactory#getBean(String) 方法中, AbstractBeanFactoryDefaultListableBeanFactory 的父類ide

@Override
public Object getBean(String name) throws BeansException {  return doGetBean(name, null, null, false); } 複製代碼

能夠看到 do 開頭的纔是真正幹活的老大 , AbstractBeanFactory#doGetBean源碼分析

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,  @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {   // 找到這個參數的 bean name  final String beanName = transformedBeanName(name);  Object bean;   // Eagerly check singleton cache for manually registered singletons.  // 檢查緩衝中是否有這個bean、spring中只是保存單例的bean  Object sharedInstance = getSingleton(beanName);  if (sharedInstance != null && args == null) {  // 這裏被我刪除了一些spring 的log  // 處理一下 factory bean 的狀況、包括從 factory beans 的緩存中獲取、或者從新調用 factory bean 的 get bean 方法 包括一些回調  bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);  }  ..........  ........... 複製代碼

由於這個方法實在太長了因此截取一部分、咱們一步步來分析post

transformedBeanName(name) 這個方法就是將咱們的 name 轉換爲真正的 beanName,由於咱們傳進來的參數多是一個 alias 或者多是一個 factoryBean 的 beanName (前綴爲&),而咱們在 Spring 中存放的 factoryBean 的 beanName 是沒有 & 前綴的,因此須要處理掉這個前綴

protected String transformedBeanName(String name) {
 return canonicalName(BeanFactoryUtils.transformedBeanName(name)); } public static String transformedBeanName(String name) {  Assert.notNull(name, "'name' must not be null");  // 是不是一個 factory bean 、若是不是的話就直接返回  if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {  return name;  }  // 若是是的話就將其前綴 & 去掉  return transformedBeanNameCache.computeIfAbsent(name, beanName -> {  do {  beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());  }  while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));  return beanName;  });  }  /**  * 找到這個別名的最終的 bean Name、若是沒有的話(  * 也就是說參數中的name 就是人家的 bean name 那麼就直接返回這個 參數就好了)  *  */  public String canonicalName(String name) {  String canonicalName = name;  // Handle aliasing...  String resolvedName;  do {  resolvedName = this.aliasMap.get(canonicalName);  if (resolvedName != null) {  canonicalName = resolvedName;  }  }  while (resolvedName != null);  return canonicalName;  } 複製代碼

讓咱們再看看下一個方法 DefaultSingletonBeanRegistry#getSingleton(String)

public Object getSingleton(String beanName) {
 // allowEarlyReference 容許早期依賴  return getSingleton(beanName, true); } 複製代碼
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {   Object singletonObject = this.singletonObjects.get(beanName);  // 這個bean 正處於 建立階段  if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {  // 併發控制  synchronized (this.singletonObjects) {  // 單例緩存是否存在  singletonObject = this.earlySingletonObjects.get(beanName);  // 是否運行獲取 bean factory 建立出的 bean  if (singletonObject == null && allowEarlyReference) {  // 獲取緩存中的 ObjectFactory  ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);  if (singletonFactory != null) {  singletonObject = singletonFactory.getObject();  // 將對象緩存到 earlySingletonObject中  this.earlySingletonObjects.put(beanName, singletonObject);  // 從工廠緩衝中移除  this.singletonFactories.remove(beanName);  }  }  }  }  return singletonObject; } 複製代碼

上面的代碼就是 Spring 嘗試從緩存中加載單例。單例在 Spring 的同一個容器中只會被建立一次,後續再獲取 bean,就直接從緩存中取了。

在介紹這個方法以前、咱們先認識下 DefaultSingletonBeanRegistry 這個類裏面的成員變量吧

  • Map<String, Object> singletonObjects 這個很好理解、 key 就是 beanName ,value 就是 bean 實例
  • Map<String, ObjectFactory<?>> singletonFactories key 爲 beanName,value 爲建立 bean 的工廠
  • Map<String, Object> earlySingletonObjects key 爲 beanName ,value 爲 bean。可是和 singletonObjects 不一樣的是,bean 被加入到 earlySingletonObjects 的時候、這個 bean 仍是處於一種建立中的狀態,目的也很簡單、Spring 用來解決某些場景下的循環依賴

咱們再回到代碼中、分析一下它的邏輯

  1. 先從 singletonObjects 中嘗試獲取 bean,這裏存放的是已經建立好的 bean 了、若是在這裏能知道、那固然是最好啦
  2. 若是在這裏找不到的話、那麼咱們就要判斷下這個 beanName 對應的 bean 是否正在建立中
  3. 若是是的話,那麼咱們再看看這個正在建立的 bean 是否已經曝光出來、若是沒有的話、那麼就要看看咱們的參數是否容許依賴早期的 bean 了、
  4. 若是容許早期依賴、那麼咱們就嘗試衝 ObjectFactory 中獲取到對應的 bean、並將它放入到 earlySingletonObjects 中、並從 singletonFactories 中移除

相似多級緩存的設計

在上面的方法中咱們看到 isSingletonCurrentlyInCreation(beanName) 這個方法、

public boolean isSingletonCurrentlyInCreation(String beanName) {
 return this.singletonsCurrentlyInCreation.contains(beanName); } 複製代碼

singletonsCurrentlyInCreation 這個 Set 中,當建立一個 bean 以前會將其 對應的 beanName 放置到這個 Set 中、後面的分析會涉及到、這裏先提一嘴

debug
debug

咱們第一次獲取這個 bean 、返回爲 null 是正常的

那假如咱們在代碼中 getBean 了兩次

defaultListableBeanFactory.getBean("com.demo.data.Person#0")
defaultListableBeanFactory.getBean("com.demo.data.Person#0") 複製代碼

那麼針對第二次的調用、返回的值就不是爲 null 了

Object sharedInstance = getSingleton(beanName);
 if (sharedInstance != null && args == null) {  // 處理一下 factory bean 的狀況、包括從 factory beans 的緩存中獲取、或者從新調用 factory bean 的 get bean 方法 包括一些回調  bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);  } 複製代碼

咱們先假設 sharedInstance 不爲 null 也就是咱們第二次調用 getBean ,咱們進入到 getObjectForBeanInstance 方法中

protected Object getObjectForBeanInstance(  Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {   // 我王大錘就是想要一個 factory bean  if (BeanFactoryUtils.isFactoryDereference(name)) {   // 若是這個是 NullBean 類型、表示這是一個 null 的 instance、直接返回  if (beanInstance instanceof NullBean) {  return beanInstance;  }  // 獲取到的 beanInstance 不是一個 factory、可是你tm name 又帶有這個 & 很迷惑啊兄弟  if (!(beanInstance instanceof FactoryBean)) {  throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());  }   if (mbd != null) {  mbd.isFactoryBean = true;  }  return beanInstance;  }   // 王大錘不想要factory bean、而且spring 也幫他找到了一個普通的 bean、直接返回  if (!(beanInstance instanceof FactoryBean)) {  return beanInstance;  }   // 王大錘要的是一個普通的bean 、可是spring 給他找到了一個 factory的bean、那麼spring 是否是要作一些額外的處理 給王大錘返回一個普通的bean  Object object = null;   if (mbd != null) {  mbd.isFactoryBean = true;  } else {  // 從緩存中 看看有沒有  object = getCachedObjectForFactoryBean(beanName);  }   // 若是 bean factory 中仍是沒有  if (object == null) {  // Return bean instance from factory.  FactoryBean<?> factory = (FactoryBean<?>) beanInstance;  // 從 緩存中拿到 bean definition  if (mbd == null && containsBeanDefinition(beanName)) {  mbd = getMergedLocalBeanDefinition(beanName);  }  // 是不是用戶定義的仍是程序自己須要建立的bean  boolean synthetic = (mbd != null && mbd.isSynthetic());   object = getObjectFromFactoryBean(factory, beanName, !synthetic);  }  return object; } 複製代碼

咱們按步驟分析下上面的代碼

  1. 咱們調用 getBean(name) 中的 name 若是包含前綴 & ,表面咱們是想要從 Spring 中獲取一個 FactoryBean ,那麼咱們就要判斷咱們從緩存中獲取的 beanInstance 是不是 一個 FactoryBean 、若是是的話就直接返回不是的話就要拋出異常了
  2. 咱們想要的是一個非 factoryBean 而且 在 spring 容器中找到了非 factoryBean 的 bean、那麼就直接返回
  3. 咱們想要的是一個 非 factoryBean 可是在 spring 容器中找到了一個 factoryBean 的 bean、那麼就要進入到 getObjectFromFactoryBean 方法中了
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
 // 爲單例模式且緩存中存在  if (factory.isSingleton() && containsSingleton(beanName)) {   synchronized (getSingletonMutex()) {  // 從緩存中獲取指定的 bean(這個bean 是從 factory bean 建立出來的)  Object object = this.factoryBeanObjectCache.get(beanName);   if (object == null) {  // 爲空則從 factory bean 中獲取對象  object = doGetObjectFromFactoryBean(factory, beanName);  // 從緩存中獲取  Object alreadyThere = this.factoryBeanObjectCache.get(beanName);  if (alreadyThere != null) {  // 已經存放到 緩存中了、後續的操做就不須要了  object = alreadyThere;  } else {  // 須要作一些後置處理  if (shouldPostProcess) {  // 若是這個bean正在建立中、  if (isSingletonCurrentlyInCreation(beanName)) {  return object;  }  // 前置處理 主要是將這個bean 加入到正在建立中的隊列 singletonsCurrentlyInCreation  beforeSingletonCreation(beanName);  try {  // 對 從 factoryBean 獲取的對象進行後處理  // 生成對象將暴露給 bean 引用 並回調 beanPostProcessor  object = postProcessObjectFromFactoryBean(object, beanName);  } catch (Throwable ex) {  throw new BeanCreationException(beanName,  "Post-processing of FactoryBean's singleton object failed", ex);  } finally {  // 後置處理 將其從 singletonsCurrentlyInCreation 移除  afterSingletonCreation(beanName);  }  }  // 他的 factory bean 已經存在 緩存中了、那麼這個 factory bean 產生的bean 應該也要緩存一下  if (containsSingleton(beanName)) {  this.factoryBeanObjectCache.put(beanName, object);  }  }  }   return object;  }  } else {  // 非單例  Object object = doGetObjectFromFactoryBean(factory, beanName);   if (shouldPostProcess) {  try {  //  object = postProcessObjectFromFactoryBean(object, beanName);  } catch (Throwable ex) {  throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);  }  }  return object;  } } 複製代碼

啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊、代碼很長長長..........咱們一點點來分析

  1. 第一步就是判斷這個 factoryBean 是不是單例、若是不是的話,而且是用戶本身定義的 bean、那麼就須要調用 postProcessObjectFromFactoryBean 方法去作一個後續的處理

    1. 這裏面最終回調的就是咱們經常使用的一個接口 BeanPostProcessor
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)  throws BeansException {   Object result = existingBean;  for (BeanPostProcessor processor : getBeanPostProcessors()) {  Object current = processor.postProcessAfterInitialization(result, beanName);  if (current == null) {  return result;  }  result = current;  }  return result; } 複製代碼
  2. 若是這 beanFactory 是一個單例,那咱們就看看 factoryBeanObjectCache ( key 是 beanName,value 是 beanFactory 產生出來的 object 也是咱們正要獲取的 bean ) 這個 Map 中是否存在這個 beanName 這個 bean

  3. 若是存在的話、就直接返回、若是不存在的話、那就 doGetObjectFromFactoryBean ,從這個方法中使用 FactoryBean#getObject 產生 bean

  4. 其實下面這段代碼確實讓人看不懂哦

    Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
    if (alreadyThere != null) { // 已經存放到 緩存中了、後續的操做就不須要了 object = alreadyThere; } 複製代碼
  5. 而後咱們看到 beforeSingletonCreation 這個方法、就是上面 getSingletonisSingletonCurrentlyInCreation 判斷一個 bean 是否處於正在建立中

    protected void beforeSingletonCreation(String beanName) {
     if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {  throw new BeanCurrentlyInCreationException(beanName);  }  }  public boolean isSingletonCurrentlyInCreation(String beanName) {  return this.singletonsCurrentlyInCreation.contains(beanName);  } 複製代碼
  6. 而後又調用到 postProcessObjectFromFactoryBean 方法、最終回調的就是咱們經常使用的一個接口 BeanPostProcessor

  7. 最好調用 afterSingletonCreation(beanName) 方法、將其從 正在建立中的 bean 的集合中移除、最後的最後、將其加入到 factoryBeanObjectCache 集合中

今天咱們就先分析到這裏、後續的話咱們在後面的文章繼續探討。今天咱們大體分析了 getBean 裏面的這三個方法

image-20200530152154079
image-20200530152154079

總結

  • 根據參數中的 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

上面的三個方法大體流程就是這樣、但願對各位有幫助

有興趣進入羣聊、一塊兒交流一塊兒划水

image-20200530170736820
image-20200530170736820
此次必定?
此次必定?
相關文章
相關標籤/搜索