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
  • 一個自定義WPF窗體的解決方案,借鑒了呂毅老師的WPF製作高性能的透明背景的異形視窗一文,併在此基礎上增加了滑鼠穿透的功能。可以使得透明窗體的滑鼠事件穿透到下層,在下層窗體中響應。 ...
  • 在C#中使用RabbitMQ做個簡單的發送郵件小項目 前言 好久沒有做項目了,這次做一個發送郵件的小項目。發郵件是一個比較耗時的操作,之前在我的個人博客裡面回覆評論和友鏈申請是會通過發送郵件來通知對方的,不過當時只是簡單的進行了非同步操作。 那麼這次來使用RabbitMQ去統一發送郵件,我的想法是通過 ...
  • 當你使用Edge等瀏覽器或系統軟體播放媒體時,Windows控制中心就會出現相應的媒體信息以及控制播放的功能,如圖。 SMTC (SystemMediaTransportControls) 是一個Windows App SDK (舊為UWP) 中提供的一個API,用於與系統媒體交互。接入SMTC的好 ...
  • 最近在微軟商店,官方上架了新款Win11風格的WPF版UI框架【WPF Gallery Preview 1.0.0.0】,這款應用引入了前沿的Fluent Design UI設計,為用戶帶來全新的視覺體驗。 ...
  • 1.簡單使用實例 1.1 添加log4net.dll的引用。 在NuGet程式包中搜索log4net並添加,此次我所用版本為2.0.17。如下圖: 1.2 添加配置文件 右鍵項目,添加新建項,搜索選擇應用程式配置文件,命名為log4net.config,步驟如下圖: 1.2.1 log4net.co ...
  • 之前也分享過 Swashbuckle.AspNetCore 的使用,不過版本比較老了,本次演示用的示例版本為 .net core 8.0,從安裝使用開始,到根據命名空間分組顯示,十分的有用 ...
  • 在 Visual Studio 中,至少可以創建三種不同類型的類庫: 類庫(.NET Framework) 類庫(.NET 標準) 類庫 (.NET Core) 雖然第一種是我們多年來一直在使用的,但一直感到困惑的一個主要問題是何時使用 .NET Standard 和 .NET Core 類庫類型。 ...
  • WPF的按鈕提供了Template模板,可以通過修改Template模板中的內容對按鈕的樣式進行自定義。結合資源字典,可以將自定義資源在xaml視窗、自定義控制項或者整個App當中調用 ...
  • 實現了一個支持長短按得按鈕組件,單擊可以觸發Click事件,長按可以觸發LongPressed事件,長按鬆開時觸發LongClick事件。還可以和自定義外觀相結合,實現自定義的按鈕外形。 ...
  • 一、WTM是什麼 WalkingTec.Mvvm框架(簡稱WTM)最早開發與2013年,基於Asp.net MVC3 和 最早的Entity Framework, 當初主要是為瞭解決公司內部開發效率低,代碼風格不統一的問題。2017年9月,將代碼移植到了.Net Core上,併進行了深度優化和重構, ...