Spring 源碼(14)Spring Bean 的創建過程(5)

来源:https://www.cnblogs.com/redwinter/archive/2022/05/13/16268667.html
-Advertisement-
Play Games

到目前為止,我們知道Spring創建Bean對象有5中方法,分別是: 使用FactoryBean的getObject方法創建 使用BeanPostProcessor的子介面InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiati ...


到目前為止,我們知道Spring創建Bean對象有5中方法,分別是:

  • 使用FactoryBeangetObject方法創建
  • 使用BeanPostProcessor的子介面InstantiationAwareBeanPostProcessorpostProcessBeforeInstantiation方法創建
  • 設置BeanDefinitionSupplier屬性進行創建
  • 設置BeanDefinitionfactory-method進行創建
  • 使用全過程:getBean-->doGetBean-->createBean-->doCreateBean 反射進行創建

前面4中已經介紹,接下來介紹第5種,我們知道如果使用反射創建,那麼必然要知道使用構造函數進行實例化,因為使用構造函數能夠將帶有參數的設置進去。

SmartInstantiationAwareBeanPostProcessor 介面

在前面講過InstantiationAwareBeanPostProcessor 是用來提前實例化對象的,而SmartInstantiationAwareBeanPostProcessorInstantiationAwareBeanPostProcessor 的子介面,他是用來幹啥呢?

createBeanInstance方法中的源碼:

// 省略代碼....
// 明確構造器從BeanPostProcessor中,對應的是 AutowiredAnnotationBeanPostProcessor
// 他是 SmartInstantiationAwareBeanPostProcessor 的子類,使用determineCandidateConstructors進行
// 解析構造函數
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
    mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
  return autowireConstructor(beanName, mbd, ctors, args);
}
// 省略代碼....

點進去:

protected Constructor<?>[] determineConstructorsFromBeanPostProcessors(@Nullable Class<?> beanClass, String beanName)
  throws BeansException {

  if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) {
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
      if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
        SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
        // 決定候選的構造函數
        Constructor<?>[] ctors = ibp.determineCandidateConstructors(beanClass, beanName);
        if (ctors != null) {
          return ctors;
        }
      }
    }
  }
  return null;
}

可以看到這個介面是用來解析BeanClass的構造函數的,SmartInstantiationAwareBeanPostProcessor的實現類AutowiredAnnotationBeanPostProcessor,這個類是用來解析確定合適的構造函數,重點解析了@Autowired註解,並且還解析了@Value註解和@Lookup註解。

當解析出來構造函數之後,那麼就調用autowireConstructor方法進行實例化,解析時會new一個構造器解析器ConstructorResolver ,在解析factoryMehod時也是使用的這個類使用的是instantiateUsingFactoryMethod這個方法,並且解析factoryMethod更加複雜,需要判斷是否是靜態的工廠創建還是實例工廠創建,而自動裝配的構造解析相對來說簡單一些,使用autowireConstructor方法進行解析。

最終解析出構造方法和構造參數之後進行實例化:

// 使用合適的構造方法和構造參數進行實例化
bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));

實例化:

private Object instantiate(
  String beanName, RootBeanDefinition mbd, Constructor<?> constructorToUse, Object[] argsToUse) {

  try {
    // 獲取實例化策略,一般使用 CglibSubClassingInstantiationStrategy
    InstantiationStrategy strategy = this.beanFactory.getInstantiationStrategy();
    if (System.getSecurityManager() != null) {
      return AccessController.doPrivileged((PrivilegedAction<Object>) () ->
                                           strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse),
                                           this.beanFactory.getAccessControlContext());
    }
    else {
      // 開始實例化
      return strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse);
    }
  }
  catch (Throwable ex) {
    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Bean instantiation via constructor failed", ex);
  }
}

public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
                          final Constructor<?> ctor, Object... args) {
  if (!bd.hasMethodOverrides()) {
    if (System.getSecurityManager() != null) {
      // use own privileged to change accessibility (when security is on)
      AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
        ReflectionUtils.makeAccessible(ctor);
        return null;
      });
    }
    // 實例化類,反射調用
    return BeanUtils.instantiateClass(ctor, args);
  }
  else {
    // 如果方法被覆蓋,lookup-method 和 replace-method
    return instantiateWithMethodInjection(bd, beanName, owner, ctor, args);
  }
}

如果前面的解析都沒有到Bean,那麼就會使用無參構造函數進行解析:

// 省略代碼....
// Preferred constructors for default construction?
// 首選的構造器為預設的創建方式,使用了@Primary註解的為首選的創建對象方式
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
  return autowireConstructor(beanName, mbd, ctors, null);
}

// No special handling: simply use no-arg constructor.
// 調用無參構造函數實例化對象
return instantiateBean(beanName, mbd);

實例化Bean

protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {
  try {
    Object beanInstance;
    if (System.getSecurityManager() != null) {
      beanInstance = AccessController.doPrivileged(
        (PrivilegedAction<Object>) () -> getInstantiationStrategy().instantiate(mbd, beanName, this),
        getAccessControlContext());
    }
    else {
      // 實例化對象,使用反射進行創建
      beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);
    }
    // 創建一個Bean的包裝器
    BeanWrapper bw = new BeanWrapperImpl(beanInstance);
    // 初始化Bean的包裝器
    initBeanWrapper(bw);
    return bw;
  }
  catch (Throwable ex) {
    throw new BeanCreationException(
      mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
  }
}

這裡可以看到前面使用factoryMethodautowireConstructor 解析構造函數進行實例化還是使用無參構造函數進行實例化都是將Bean進行了包裝,那這個包裝有啥作用呢?

BeanWrapper的作用

我們先來看下前面的方法是怎麼創建BeanWrapper的:

factory-method 解析,ConstructorResolver#instantiateUsingFactoryMethod 方法:

public BeanWrapper instantiateUsingFactoryMethod(
			String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
		// 創建一個Bean的包裝器
		BeanWrapperImpl bw = new BeanWrapperImpl();
		this.beanFactory.initBeanWrapper(bw);
		// factoryBean
		Object factoryBean;
		// factory 工廠類
		Class<?> factoryClass;
		// 標識是否是靜態的工廠
		boolean isStatic;
        // 省略代碼....
}  

SmartInstantiationAwareBeanPostProcessor子類AutowiredAnnotationBeanPostProcessor 解析出構造函數,然後使用ConstructorResolver#autowireConstructor 執行:

public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
			@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
		// 創建一個包裝器
		BeanWrapperImpl bw = new BeanWrapperImpl();
		// 初始化包裝器
		this.beanFactory.initBeanWrapper(bw);
		// 構造函數
		Constructor<?> constructorToUse = null;
		// 構造參數
		ArgumentsHolder argsHolderToUse = null;
		// 需要使用的構造參數
		Object[] argsToUse = null;
		// 明確的構造參數不為空,則賦值給將要執行實例化的構造參數
		if (explicitArgs != null) {
			argsToUse = explicitArgs;
		}
        // 省略代碼....
}

最終都是會進行轉換服務ConversionServicePropertyEditorRegistry的註冊,一個是用來進行屬性類型轉換的,一個是用來屬性值解析的:

protected void initBeanWrapper(BeanWrapper bw) {
  // 獲取轉換服務放到bean的包裝器中
  bw.setConversionService(getConversionService());
  // 註冊定製的屬性編輯器
  registerCustomEditors(bw);
}

在前面的文章中,介紹了這兩個如何使用,而且還自定義了屬性編輯器和類型轉換,需要的小伙伴可以去看看:

https://www.cnblogs.com/redwinter/p/16167214.htmlhttps://www.cnblogs.com/redwinter/p/16241328.html

到這裡Bean的實例化就完成了,接著往下看源碼:

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
  throws BeanCreationException {

  // Instantiate the bean.
  BeanWrapper instanceWrapper = null;
  // 從緩存中獲取FactoryBean的Bean對象
  if (mbd.isSingleton()) {
    instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
  }
  if (instanceWrapper == null) {
    // 實例化對象
    instanceWrapper = createBeanInstance(beanName, mbd, args);
  }
  // 從包裝器中獲取Bean對象
  Object bean = instanceWrapper.getWrappedInstance();
  // 從包裝器中獲取Bean類型
  Class<?> beanType = instanceWrapper.getWrappedClass();
  if (beanType != NullBean.class) {
    mbd.resolvedTargetType = beanType;
  }

  // Allow post-processors to modify the merged bean definition.
  synchronized (mbd.postProcessingLock) {
    if (!mbd.postProcessed) {
      try {
        // 合併Bean
        applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
      }
      catch (Throwable ex) {
        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                        "Post-processing of merged bean definition failed", ex);
      }
      mbd.postProcessed = true;
    }
  }
}

點進去:

protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
  for (BeanPostProcessor bp : getBeanPostProcessors()) {
    if (bp instanceof MergedBeanDefinitionPostProcessor) {
      MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
      // 執行合併BeanDefinition
      bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
    }
  }
}

可以看到這裡出現了一個介面MergedBeanDefinitionPostProcessor,這個介面也是BeanPostProcessor的子介面,那他到底是幹啥用的呢?

MergedBeanDefinitionPostProcessor 介面

點擊發現這個介面的實現類全是跟註解相關的,而最重要的是CommonAnnotationBeanPostProcessor實現類,在構造函數中設置了兩個註解:@PostConstruct@PreDestroy ,一個是在初始化完之後調用,一個是容器銷毀時調用。

未完待續.....


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、引言 CTE(Common Table Expression) 公用表達式,它是在單個語句的執行範圍內定義的臨時結果集,只在查詢期間有效。它可以自引用,也可在同一查詢中多次引用,實現了代碼段的重覆利用。 CTE最大的好處是提升T-Sql代碼的可讀性,可以以更加優雅簡潔的方式實現遞歸等複雜的查詢。 ...
  • 本文帶你瞭解蘋果 AppStore 的財年和賬單周期,關於 AppStore 開發者賬單和收入,相信很多開發者不一定有接觸,或者接觸時還是有很多疑問沒有時間來學習。另外,還會有一些財年的詭計問題,比如為什麼阿裡巴巴財年是從4月1號到次年的3月31號呢?蘋果財年為什麼這麼奇怪,本文一一為你解答~ ...
  • 5月12日晚上19點,知識賦能第五期第四節課《OpenHarmony標準系統多媒體子系統之音頻解讀》,在OpenHarmony開發者成長計劃社群內成功舉行。 ...
  • 大家好,我是半夏👴,一個剛剛開始寫文的沙雕程式員.如果喜歡我的文章,可以關註➕ 點贊 👍 加我微信:frontendpicker,一起學習交流前端,成為更優秀的工程師~關註公眾號:搞前端的半夏,瞭解更多前端知識! 點我探索新世界! 原文鏈接 ==>http://sylblog.xin/archi ...
  • 最近碰到了非同步編程的問題,決定從原理開始重新擼一遍,徹底弄懂非同步編程。 1.非同步編程思想 非同步編程是為瞭解決同步模式的一些痛點,同步模式中任務是依次執行,後一個任務必須要等待前一個任務結束後才能開始執行,當某個函數耗時過長時就可能造成頁面的假死和卡頓,而非同步編程中,後一個任務不會去等待前一個任務結束 ...
  • 《聲生不息》 是芒果TV、香港電視廣播有限公司和湖南衛視聯合推出的港樂競唱獻禮節目,聽著音樂仿佛回到了那個令人懷念的港風席卷整個亞洲的年代。該節目 Logo 採用經典紅藍配色,無限符號 ∞ 造型,滿滿的設計感。本文在僅採用原生 CSS 的情況下,儘量還原實現該 Logo 造型,本文內容雖然非常簡單,... ...
  • 翻譯自 Tim Sneath 2022年5月12日的文章 《Introducing Flutter 3》 作者 : Tim Sneath 翻譯 : 沙漠盡頭的狼(谷歌翻譯加持) 鏈接 : Introducing Flutter 3(英文原文) 我們在手機、桌面和網站開發上進行多平臺UI開發的歷程達到 ...
  • 1.“new”有什麼不對勁? 在我們沒有接觸到工廠模式(簡單工廠、工廠方法模式、抽象工廠模式)之前,我們實例化對象唯一的方法就是通過“new”關鍵字來完成。但是,大量的使用“new”關鍵字來實例化對象會違背一些設計原則,因為代碼與具體的類型綁在一起,從而導致過多的依賴於細節而非抽象,這樣代碼就很難適 ...
一周排行
    -Advertisement-
    Play Games
  • 在一些複雜的業務表中間查詢數據,有時候操作會比較複雜一些,不過基於SqlSugar的相關操作,處理的代碼會比較簡單一些,以前我在隨筆《基於SqlSugar的開發框架循序漸進介紹(2)-- 基於中間表的查詢處理》介紹過基於主表和中間表的聯合查詢,而往往實際會比這個會複雜一些。本篇隨筆介紹聯合多個表進行... ...
  • 從按鈕、文本框到下拉框、列表框,WPF提供了一系列常用控制項,每個控制項都有自己獨特的特性和用途。通過靈活的佈局容器,如網格、堆棧面板和換行面板,我們可以將這些控制項組合在一起,實現複雜的界面佈局。而通過樣式和模板,我們可以輕鬆地定製控制項的外觀和行為,以符合我們的設計需求。本篇記錄WPF入門需要瞭解的樣式... ...
  • 以MySQL資料庫為例 # 一. 安裝 NuGet搜索Dapper.Lite並安裝最新版本。 ![](https://img2023.cnblogs.com/blog/174862/202306/174862-20230602155913303-757935399.jpg) NuGet搜索MySql ...
  • # 圖片介面JWT鑒權實現 # 前言 之前做了個返回圖片鏈接的介面,然後沒做授權,然後今天鍵盤到了,也是用JWT來做介面的許可權控制。 然後JTW網上已經有很多文章來說怎麼用了,這裡就不做多的解釋了,如果不懂的可以參考下列鏈接的 文章。 圖片介面文章:[還在愁個人博客沒有圖片放?](https://w ...
  • ![線程各屬性縱覽](https://img2023.cnblogs.com/blog/1220983/202306/1220983-20230603114109107-477345835.png) 如上圖所示,線程有四個屬性: - 線程ID - 線程名稱 - 守護線程 - 線程優先順序 ### 1. ...
  • 本次主要介紹golang中的標準庫`bytes`,基本上參考了 [位元組 | bytes](https://cloud.tencent.com/developer/section/1140520) 、[Golang標準庫——bytes](https://www.jianshu.com/p/e6f7f2 ...
  • 歡迎來到本篇文章!通過上一篇什麼是 Spring?為什麼學它?的學習,我們知道了 Spring 的基本概念,知道什麼是 Spring,以及為什麼學習 Spring。今天,這篇就來說說 Spring 中的核心概念之一 IoC。 ...
  • # 2022版本IDEA+Maven+Tomcat的第一個程式(傻瓜教學) ​ 作為學習Javaweb的一個重要環節,如何實現在IDEA中利用Maven工具創建一個Javaweb程式模版並連接Tomcat發佈是非常重要的。我比較愚鈍(小白),而且自身電腦先前運行過spring或maven的程式,系統 ...
  • 本篇專門扯一下有關 QCheckBox 組件的一個問題。老周不水字數,直接上程式,你看了就明白。 #include <QApplication> #include <QWidget> #include <QPushButton> #include <QCheckBox> #include <QVBo ...
  • # 1.列表數據元素排序 在創建的列表中,數據元素的排列順序常常是無法預測的。這雖然在大多數情況下都是不可避免的,但經常需要以特定的順序呈現信息。有時候希望保留列表數據元素最初的排列順序,而有時候又需要調整排列順序。python提供了很多列表數據元素排序的方式,可根據情況選用。 ## 1.永久性排序 ...