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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...