欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺析Spring容器原始Bean是如何創(chuàng)建的

 更新時間:2023年08月02日 11:32:04   作者:江南一點雨  
這篇文章主要是想和小伙伴們一起聊聊?Spring?容器創(chuàng)建?Bean?最最核心的?createBeanInstance?方法,文中的示例代碼講解詳細,需要的可以參考一下

以下內(nèi)容基于 Spring6.0.4。

這個話題其實非常龐大,我本來想從 getBean 方法講起,但一想這樣講完估計很多小伙伴就懵了,所以我們還是一步一步來,今天我主要是想和小伙伴們講講 Spring 容器創(chuàng)建 Bean 最最核心的 createBeanInstance 方法,這個方法專門用來創(chuàng)建一個原始 Bean 實例。

松哥這里就以 Spring 源碼中方法的執(zhí)行順序為例來和小伙伴們分享。

1. doCreateBean

AbstractAutowireCapableBeanFactory#doCreateBean 就是 Bean 的創(chuàng)建方法,但是 Bean 的創(chuàng)建涉及到的步驟非常多,包括各種需要調(diào)用的前置后置處理器方法,今天我主要是想和大家聊聊單純的創(chuàng)建 Bean 的過程,其他方法咱們后面文章繼續(xù)。

在 doCreateBean 方法中,有如下一行方法調(diào)用:

protected?Object?doCreateBean(String?beanName,?RootBeanDefinition?mbd,?@Nullable?Object[]?args)
??throws?BeanCreationException?{
?//?Instantiate?the?bean.
?BeanWrapper?instanceWrapper?=?null;
?if?(mbd.isSingleton())?{
??instanceWrapper?=?this.factoryBeanInstanceCache.remove(beanName);
?}
?if?(instanceWrapper?==?null)?{
??instanceWrapper?=?createBeanInstance(beanName,?mbd,?args);
?}
?Object?bean?=?instanceWrapper.getWrappedInstance();
????//...
?return?exposedObject;
}

createBeanInstance 這個方法就是真正的根據(jù)我們的配置去創(chuàng)建一個 Bean 了。

2. createBeanInstance

先來看源碼:

protected?BeanWrapper?createBeanInstance(String?beanName,?RootBeanDefinition?mbd,?@Nullable?Object[]?args)?{
?//?Make?sure?bean?class?is?actually?resolved?at?this?point.
?Class<?>?beanClass?=?resolveBeanClass(mbd,?beanName);
?if?(beanClass?!=?null?&&?!Modifier.isPublic(beanClass.getModifiers())?&&?!mbd.isNonPublicAccessAllowed())?{
??throw?new?BeanCreationException(mbd.getResourceDescription(),?beanName,
????"Bean?class?isn't?public,?and?non-public?access?not?allowed:?"?+?beanClass.getName());
?}
?Supplier<?>?instanceSupplier?=?mbd.getInstanceSupplier();
?if?(instanceSupplier?!=?null)?{
??return?obtainFromSupplier(instanceSupplier,?beanName);
?}
?if?(mbd.getFactoryMethodName()?!=?null)?{
??return?instantiateUsingFactoryMethod(beanName,?mbd,?args);
?}
?//?Shortcut?when?re-creating?the?same?bean...
?boolean?resolved?=?false;
?boolean?autowireNecessary?=?false;
?if?(args?==?null)?{
??synchronized?(mbd.constructorArgumentLock)?{
???if?(mbd.resolvedConstructorOrFactoryMethod?!=?null)?{
????resolved?=?true;
????autowireNecessary?=?mbd.constructorArgumentsResolved;
???}
??}
?}
?if?(resolved)?{
??if?(autowireNecessary)?{
???return?autowireConstructor(beanName,?mbd,?null,?null);
??}
??else?{
???return?instantiateBean(beanName,?mbd);
??}
?}
?//?Candidate?constructors?for?autowiring?
?Constructor<?>[]?ctors?=?determineConstructorsFromBeanPostProcessors(beanClass,?beanName);
?if?(ctors?!=?null?||?mbd.getResolvedAutowireMode()?==?AUTOWIRE_CONSTRUCTOR?||
???mbd.hasConstructorArgumentValues()?||?!ObjectUtils.isEmpty(args))?{
??return?autowireConstructor(beanName,?mbd,?ctors,?args);
?}
?//?Preferred?constructors?for?default?construction?
?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 的創(chuàng)建方法了,因此這個方法我來和大家詳細分析一下。

2.1 resolveBeanClass

這個方法是用來解析出來當(dāng)前的 beanClass 對象,它的核心邏輯就是根據(jù)我們在 XML 文件中配置的類的全路徑,通過反射加載出來這個 Class

@Nullable
protected?Class<?>?resolveBeanClass(RootBeanDefinition?mbd,?String?beanName,?Class<?>...?typesToMatch)
??throws?CannotLoadBeanClassException?{
????if?(mbd.hasBeanClass())?{
????????return?mbd.getBeanClass();
????}
????return?doResolveBeanClass(mbd,?typesToMatch);
}

首先會調(diào)用 mbd.hasBeanClass() 方法去判斷是否已經(jīng)通過反射加載出來 beanClass 了,如果加載出來了就直接返回,沒有加載的話,就繼續(xù)執(zhí)行下面的 doResolveBeanClass 去加載。

什么時候會走 if 這條線呢?松哥舉一個例子,如果我們設(shè)置某一個 Bean 的 Scope 是 prototype 的話,那么當(dāng)?shù)诙潍@取該 Bean 的實例的時候,就會走 if 這條線。

@Nullable
private?Class<?>?doResolveBeanClass(RootBeanDefinition?mbd,?Class<?>...?typesToMatch)
??throws?ClassNotFoundException?{
?//...
?String?className?=?mbd.getBeanClassName();
?if?(className?!=?null)?{
??Object?evaluated?=?evaluateBeanDefinitionString(className,?mbd);
??if?(!className.equals(evaluated))?{
???//?A?dynamically?resolved?expression,?supported?as?of?4.2...
???if?(evaluated?instanceof?Class<?>?clazz)?{
????return?clazz;
???}
???else?if?(evaluated?instanceof?String?str)?{
????className?=?str;
????freshResolve?=?true;
???}
???else?{
????throw?new?IllegalStateException("Invalid?class?name?expression?result:?"?+?evaluated);
???}
??}
??if?(freshResolve)?{
???//?When?resolving?against?a?temporary?class?loader,?exit?early?in?order
???//?to?avoid?storing?the?resolved?Class?in?the?bean?definition.
???if?(dynamicLoader?!=?null)?{
????????????????return?dynamicLoader.loadClass(className);
???}
???return?ClassUtils.forName(className,?dynamicLoader);
??}
?}
?//?Resolve?regularly,?caching?the?result?in?the?BeanDefinition...
?return?mbd.resolveBeanClass(beanClassLoader);
}

按理說,根據(jù)我們配置的類的全路徑加載出來一個 Class 應(yīng)該是非常容易的,直接 Class.forName 就可以了。

但是!??!

如果對 Spring 用法比較熟悉的小伙伴就知道,配置 Class 全路徑的時候,我們不僅可以像下面這樣老老實實配置:

<bean?class="org.javaboy.bean.Book"/>

我們甚至可以使用 SpEL 來配置 Bean 名稱,例如我有如下類:

public?class?BeanNameUtils?{
????public?String?getName()?{
????????return?"org.javaboy.bean.User";
????}
}

這里有一個 getName 方法,這個方法返回的是一個類的全路徑,現(xiàn)在我們在 XML 文件中可以這樣配置:

<bean?class="org.javaboy.bean.BeanNameUtils"?id="beanNameUtils"/>
<bean?class="#{beanNameUtils.name}"?id="user"/>

在 XML 的 class 屬性中,我們可以直接使用 SpEL 去引用一個方法的執(zhí)行,用該方法的返回值作為 class 的值。

了解了 Spring 中的這個玩法,再去看上面的源碼就很好懂了:

首先調(diào)用 mbd.getBeanClassName(); 去獲取到類路徑。

接下來調(diào)用 evaluateBeanDefinitionString 方法進行 SpEL 運算,這個運算的目的是為了解析 className 中的 SpEL 表達式,當(dāng)然,一般情況下 className 就是一個普通的字符串,不是 SpEL 表達式,那么解析完成之后就還是原本的字符串。如果是 className 是一個 SpEL,那么合法的解析結(jié)果分為兩種:

  • 首先就是解析之后拿到了一個 Class,那這個就是我們想要的結(jié)果,直接返回即可。
  • 要么就是解析出來是一個字符串,松哥上面舉的例子就是這種情況,那么就把這個字符串賦值給 className,并且將 freshResolve 屬性設(shè)置為 true,然后在接下來的 if 分支中去加載 Class。

當(dāng)然,上面這些都是處理特殊情況,一般我們配置的普通 Bean,都是直接走最后一句 mbd.resolveBeanClass(beanClassLoader),這個方法的邏輯其實很好懂,我把代碼貼出來小伙伴們來瞅一瞅:

@Nullable
public?Class<?>?resolveBeanClass(@Nullable?ClassLoader?classLoader)?throws?ClassNotFoundException?{
?String?className?=?getBeanClassName();
?if?(className?==?null)?{
??return?null;
?}
?Class<?>?resolvedClass?=?ClassUtils.forName(className,?classLoader);
?this.beanClass?=?resolvedClass;
?return?resolvedClass;
}

這個方法就相當(dāng)直白了,根據(jù) className 加載出來 Class 對象,然后給 beanClass 屬性也設(shè)置上值,這就和一開始的 if (mbd.hasBeanClass()) 對應(yīng)上了。

好了,到此,我們總算是根據(jù) className 拿到 Class 對象了。

2.2 Supplier 和 factory-method

好了,回到一開始的源碼中,接下來該執(zhí)行如下兩行代碼了:

Supplier<?>?instanceSupplier?=?mbd.getInstanceSupplier();
if?(instanceSupplier?!=?null)?{
?return?obtainFromSupplier(instanceSupplier,?beanName);
}
if?(mbd.getFactoryMethodName()?!=?null)?{
?return?instantiateUsingFactoryMethod(beanName,?mbd,?args);
}

這兩個松哥在前面的文章中和小伙伴們已經(jīng)講過了(Spring5 中更優(yōu)雅的第三方 Bean 注入):前面的 obtainFromSupplier 方法是 Spring5 開始推出來的 Supplier,通過回調(diào)的方式去獲取一個對象;第二個方法 instantiateUsingFactoryMethod 則是通過配置的 factory-method 來獲取到一個 Bean 實例。

對這兩個方法不熟悉的小伙伴可以參考前面的文章:Spring5 中更優(yōu)雅的第三方 Bean 注入。

2.3 re-create 邏輯

繼續(xù)回到一開始的源碼中,接下來是一段 re-create 的處理邏輯,如下:

boolean?resolved?=?false;
boolean?autowireNecessary?=?false;
if?(args?==?null)?{
?synchronized?(mbd.constructorArgumentLock)?{
??if?(mbd.resolvedConstructorOrFactoryMethod?!=?null)?{
???resolved?=?true;
???autowireNecessary?=?mbd.constructorArgumentsResolved;
??}
?}
}
if?(resolved)?{
?if?(autowireNecessary)?{
??return?autowireConstructor(beanName,?mbd,?null,?null);
?}
?else?{
??return?instantiateBean(beanName,?mbd);
?}
}

根據(jù)前面的介紹,我們現(xiàn)在已經(jīng)獲取到 Class 對象了,接下來直接調(diào)用相應(yīng)的構(gòu)造方法就可以獲取到 Bean 實例了。但是這個 Class 對象可能存在多個構(gòu)造方法,所以還需要一堆流程去確定到底調(diào)用哪個構(gòu)造方法。

所以這里會先去判斷 resolvedConstructorOrFactoryMethod 是否不為空,不為空的話,說明這個 Bean 之前已經(jīng)創(chuàng)建過了,該用什么方法創(chuàng)建等等問題都已經(jīng)確定了,所以這次就不用重新再去確定了(resolved = true)。另一方面,autowireNecessary 表示構(gòu)造方法的參數(shù)是否已經(jīng)處理好了,這個屬性為 true 則表示構(gòu)造方法的參數(shù)已經(jīng)處理好了,那么就可以調(diào)用 autowireConstructor 方法去創(chuàng)建一個 Bean 出來,否則調(diào)用 instantiateBean 方法初始化 Bean。

這里涉及到的 autowireConstructor 和 instantiateBean 方法我們先不細說了,因為在后面還會再次涉及到。

2.4 構(gòu)造器注入

繼續(xù)回到一開始的源碼中,接下來就是針對各種處理器的預(yù)處理了:

Constructor<?>[]?ctors?=?determineConstructorsFromBeanPostProcessors(beanClass,?beanName);
if?(ctors?!=?null?||?mbd.getResolvedAutowireMode()?==?AUTOWIRE_CONSTRUCTOR?||
??mbd.hasConstructorArgumentValues()?||?!ObjectUtils.isEmpty(args))?{
?return?autowireConstructor(beanName,?mbd,?ctors,?args);
}

先來看 determineConstructorsFromBeanPostProcessors 方法,這個方法主要是考慮到你可能提供了 SmartInstantiationAwareBeanPostProcessor,松哥在前面的文章中和大家專門講過 BeanPostProcessor(BeanFactoryPostProcessor 和 BeanPostProcessor 有什么區(qū)別?),這里的 SmartInstantiationAwareBeanPostProcessor 算是 BeanPostProcessor 的一種,也是 Bean 的一種增強器。SmartInstantiationAwareBeanPostProcessor 中有一個 determineCandidateConstructors 方法,這個方法返回某一個 Bean 的構(gòu)造方法,將來可以通過這個構(gòu)造方法初始化某一個 Bean。

我給大家舉一個簡單例子,假設(shè)我有如下類:

public?class?User?{
????private?String?username;
????private?String?address;
????public?User()?{
????????System.out.println("=====no?args=====");
????}
????public?User(ObjectProvider<String>?username)?{
????????System.out.println("args==username");
????????this.username?=?username.getIfAvailable();
????}
????//省略?getter/setter/toString
}

現(xiàn)在我在 Spring 容器中注冊這個對象:

<bean?class="org.javaboy.bean.User"?id="user">
</bean>

按照我們已有的知識,這個將來會調(diào)用 User 的無參構(gòu)造方法去完成 User 對象的初始化。

但是現(xiàn)在,假設(shè)我添加如下一個處理器:

public?class?MySmartInstantiationAwareBeanPostProcessor?implements?SmartInstantiationAwareBeanPostProcessor?{
????@Override
????public?Constructor<?>[]?determineCandidateConstructors(Class<?>?beanClass,?String?beanName)?throws?BeansException?{
????????if?("user".equals(beanName))?{
????????????Constructor<?>?constructor?=?null;
????????????try?{
????????????????constructor?=?beanClass.getConstructor(ObjectProvider.class);
????????????}?catch?(NoSuchMethodException?e)?{
????????????????throw?new?RuntimeException(e);
????????????}
????????????return?new?Constructor[]{constructor};
????????}
????????return?SmartInstantiationAwareBeanPostProcessor.super.determineCandidateConstructors(beanClass,?beanName);
????}
}

在 determineCandidateConstructors 方法中,返回一個有參構(gòu)造方法,那么將來 Spring 容器會通過這里返回的有參構(gòu)造方法去創(chuàng)建 User 對象,而不是通過無參構(gòu)造方法去創(chuàng)建 User 對象。

最后,將這個處理器注冊到 Spring 容器:

<bean?class="org.javaboy.bean.MySmartInstantiationAwareBeanPostProcessor"/>

現(xiàn)在,當(dāng)我們啟動 Spring 容器的時候,User 就是通過有參構(gòu)造方法初始化的,而不是無參構(gòu)造方法。之所以會這樣,就是因為本小節(jié)一開始提到的源碼 determineConstructorsFromBeanPostProcessors,這個方法就是去查看有無 SmartInstantiationAwareBeanPostProcessor,如果有,就調(diào)用對應(yīng)的方法找到處理器并返回。

這個弄懂之后,if 中其他幾種情況就好理解了,mbd.getResolvedAutowireMode() 是查看當(dāng)前對象的注入方式,這個一般是在 XML 中配置的,不過日常開發(fā)中我們一般不會配置這個屬性,如果需要配置,方式如下:

<bean?class="org.javaboy.bean.User"?id="user"?autowire="constructor">
</bean>

如果添加了 autowire="constructor" 就表示要通過構(gòu)造方法進行注入,那么這里也會進入到 if 中。

if 里邊剩下的幾個條件都好說,就是看是否有配置構(gòu)造方法參數(shù),如果配置了,那么也直接調(diào)用相應(yīng)的構(gòu)造方法就行了。

這里最終執(zhí)行的是 autowireConstructor 方法,這個方法比較長,我就不貼出來了,和大家說一說它的思路:

  • 首先把能獲取到的構(gòu)造方法都拿出來,如果構(gòu)造方法只有一個,且目前也沒有任何和構(gòu)造方法有關(guān)的參數(shù),那就直接用這個構(gòu)造方法就行了。
  • 如果第一步不能解決問題,接下來就遍歷所有的構(gòu)造方法,并且和已有的參數(shù)進行參數(shù)數(shù)量和類型比對,找到合適的構(gòu)造方法并調(diào)用。

2.5 PreferredConstructors

繼續(xù)回到一開始的源碼中,接下來是這樣了:

ctors?=?mbd.getPreferredConstructors();
if?(ctors?!=?null)?{
?return?autowireConstructor(beanName,?mbd,?ctors,?null);
}

這塊代碼看字面好理解,就是獲取到主構(gòu)造方法,不過這個是針對 Kotlin 的,跟我們 Java 無關(guān),我就不啰嗦了。

2.6 instantiateBean

最后就是 instantiateBean 方法了,這個方法就比較簡單了,我把代碼貼一下小伙伴們應(yīng)該自己都能看明白:

protected?BeanWrapper?instantiateBean(String?beanName,?RootBeanDefinition?mbd)?{
?try?{
??Object?beanInstance?=?getInstantiationStrategy().instantiate(mbd,?beanName,?this);
??BeanWrapper?bw?=?new?BeanWrapperImpl(beanInstance);
??initBeanWrapper(bw);
??return?bw;
?}
?catch?(Throwable?ex)?{
??throw?new?BeanCreationException(mbd.getResourceDescription(),?beanName,?ex.getMessage(),?ex);
?}
}
@Override
public?Object?instantiate(RootBeanDefinition?bd,?@Nullable?String?beanName,?BeanFactory?owner)?{
?//?Don't?override?the?class?with?CGLIB?if?no?overrides.
?if?(!bd.hasMethodOverrides())?{
??Constructor<?>?constructorToUse;
??synchronized?(bd.constructorArgumentLock)?{
???constructorToUse?=?(Constructor<?>)?bd.resolvedConstructorOrFactoryMethod;
???if?(constructorToUse?==?null)?{
????final?Class<?>?clazz?=?bd.getBeanClass();
????if?(clazz.isInterface())?{
?????throw?new?BeanInstantiationException(clazz,?"Specified?class?is?an?interface");
????}
????try?{
?????constructorToUse?=?clazz.getDeclaredConstructor();
?????bd.resolvedConstructorOrFactoryMethod?=?constructorToUse;
????}
????catch?(Throwable?ex)?{
?????throw?new?BeanInstantiationException(clazz,?"No?default?constructor?found",?ex);
????}
???}
??}
??return?BeanUtils.instantiateClass(constructorToUse);
?}
?else?{
??//?Must?generate?CGLIB?subclass.
??return?instantiateWithMethodInjection(bd,?beanName,?owner);
?}
}

從上面小伙伴么可以看到,本質(zhì)上其實就是調(diào)用了 constructorToUse = clazz.getDeclaredConstructor();,獲取到一個公開的無參構(gòu)造方法,然后據(jù)此創(chuàng)建一個 Bean 實例出來。

3. 小結(jié)

好了,這就是 Spring 容器中 Bean 的創(chuàng)建過程,我這里單純和小伙伴們分享了原始 Bean 的創(chuàng)建這一個步驟,這塊內(nèi)容其實非常龐雜,以后有空我會再和小伙伴們分享。

最后,給上面分析的方法生成了一個時序圖,小伙伴們作為參考。

其實看 Spring 源碼,松哥最大的感悟就是小伙伴們一定要了解 Spring 的各種用法,在此基礎(chǔ)之上,源碼就很好懂,如果你只會 Spring 一些基本用法,那么源碼一定是看得云里霧里的。

以上就是淺析Spring容器原始Bean是如何創(chuàng)建的的詳細內(nèi)容,更多關(guān)于Spring創(chuàng)建容器原始Bean的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 如何解決IDEA沒有新建servlet選項問題

    如何解決IDEA沒有新建servlet選項問題

    這篇文章主要介紹了如何解決IDEA沒有新建servlet選項問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • 排序算法圖解之Java冒泡排序及優(yōu)化

    排序算法圖解之Java冒泡排序及優(yōu)化

    冒泡排序即通過對待排序的序列從前往后,依次比較相鄰元素的值,若發(fā)現(xiàn)逆序則交換位置,使較大的元素逐漸移動到后部。本文通過圖片和示例介紹了冒泡排序的實現(xiàn)及優(yōu)化,需要的可以參考一下
    2022-11-11
  • Spring自帶的校驗框架Validation的使用實例

    Spring自帶的校驗框架Validation的使用實例

    今天小編就為大家分享一篇關(guān)于Spring自帶的校驗框架Validation的使用實例,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • CentOS7和8中安裝Maven3.8.4的簡單步驟

    CentOS7和8中安裝Maven3.8.4的簡單步驟

    maven是屬于apache的一個工具,主要是對java進行編譯打包,解決依賴關(guān)系,下面這篇文章主要給大家介紹了關(guān)于CentOS7和8中安裝Maven3.8.4的相關(guān)資料,需要的朋友可以參考下
    2022-04-04
  • Java微信公眾平臺之消息管理

    Java微信公眾平臺之消息管理

    這篇文章主要為大家詳細介紹了Java微信公眾平臺之消息管理的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • Mybatis集成Spring的實例代碼_動力節(jié)點Java 學(xué)院整理

    Mybatis集成Spring的實例代碼_動力節(jié)點Java 學(xué)院整理

    這篇文章主要介紹了Mybatis集成Spring的實例代碼,需要的朋友可以參考下
    2017-09-09
  • 淺析Java如何在并發(fā)環(huán)境下生成一個只讀的map

    淺析Java如何在并發(fā)環(huán)境下生成一個只讀的map

    這篇文章主要為大家詳細介紹了Java如何在并發(fā)環(huán)境下生成一個只讀的map,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-04-04
  • Idea 自動生成測試的實現(xiàn)步驟

    Idea 自動生成測試的實現(xiàn)步驟

    當(dāng)我們在寫完一些接口方法后需要測試時,一個一個新建測試類比較麻煩 idea給我們提供了快捷辦法,本文主要介紹了Idea 自動生成測試的實現(xiàn)步驟,具有一定的參考價值,感興趣的可以了解一下
    2024-05-05
  • Java實現(xiàn)Kruskal算法的示例代碼

    Java實現(xiàn)Kruskal算法的示例代碼

    Kruskal算法是一種用來尋找最小生成樹的算法,由Joseph Kruskal在1956年發(fā)表。用來解決同樣問題的還有Prim算法和Boruvka算法等。本文將介紹用Java語言實現(xiàn)Kruskal算法的示例代碼,需要的可以參考一下
    2022-07-07
  • Java編程實現(xiàn)多線程TCP服務(wù)器完整實例

    Java編程實現(xiàn)多線程TCP服務(wù)器完整實例

    這篇文章主要介紹了Java編程實現(xiàn)多線程TCP服務(wù)器完整實例,具有一定借鑒價值,需要的朋友可以參考下
    2018-01-01

最新評論