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

Spring?BeanDefinition父子關(guān)系示例解析

 更新時間:2023年08月17日 10:08:36   作者:江南一點雨  
這篇文章主要為大家介紹了Spring?BeanDefinition父子關(guān)系示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

引言

在 Spring 框架中,BeanDefinition 是一個核心概念,用于定義和配置 bean 的元數(shù)據(jù),雖然在實際應(yīng)用中,我們一般并不會或者很少直接定義 BeanDefinition,但是,我們在 XML 文件中所作的配置,以及利用 Java 代碼做的各種 Spring 配置,都會被解析為 BeanDefinition,然后才會做進一步的處理。BeanDefinition 允許開發(fā)人員以一種聲明性的方式定義和組織 bean,這里有很多屬性,今天松哥單純的來和小伙伴們聊一聊它的 parentName 屬性,parentName 屬性在 BeanDefinition 中扮演著重要的角色,用于建立 bean 之間的父子關(guān)系。

之前有一篇文章和小伙伴們聊了 BeanFactory 之間的父子關(guān)系(Spring 中的父子容器是咋回事?),大家注意和今天的內(nèi)容進行區(qū)分,今天我們聊的是 BeanDefinition 之間的父子關(guān)系。

BeanDefinition 的 parentName 屬性的主要功能是允許我們在創(chuàng)建一個 bean 的同時,能夠繼承另一個已經(jīng)定義好的 bean。通過指定 parentName 屬性,我們可以重用已有 bean 的配置,并在此基礎(chǔ)上進行修改或擴展。

先不廢話了,我先來舉兩個例子,小伙伴們先感受一下 BeanDefinition 的作用。

1. 實踐

假設(shè)我有如下兩個類,首先是一個動物的基類,如下:

public class Animal {
    private String name;
    private Integer age;
    //省略 getter/setter
}

然后有一個 Dog 類,如下:

public class Dog {
    private String name;
    private Integer age;
    private String color;
    //省略 getter/setter
}

 小伙伴們注意,這里的 Dog 類并沒有繼承自 Animal 類,但是有兩個跟 Animal 同名的屬性。之所以這樣設(shè)計是希望小伙伴們理解 BeanDefinition 中的 parentName 屬性和 Java 中的繼承并無關(guān)系,雖然大部分情況下我們用到 parentName 的時候,Java 中相關(guān)的類都是繼承關(guān)系。

現(xiàn)在,有一些通用的屬性我想在 Animal 中進行配置,Dog 中特有的屬性則在 Dog 中進行配置,我們來看下通過 XML 和 Java 分別該如何配置。

1.1 XML 配置

<bean id="animal" class="org.javaboy.demo.p2.Animal">
    <property name="name" value="小黑"/>
    <property name="age" value="3"/>
</bean>
<bean class="org.javaboy.demo.p2.Dog" id="dog" parent="animal">
    <property name="color" value="黑色"/>
</bean>

小伙伴們看到,首先我們配置 Animal,Animal 中有 name 和 age 兩個屬性,然后我又配置了 Dog Bean,并未之指定了 parent 為 animal,然后給 Dog 設(shè)置了 color 屬性。

現(xiàn)在,Dog Bean 定義出來的 BeanDefinition 中將來就包含了 animal 中的屬性值。

1.2 Java 配置

再來看看 Java 配置該如何寫。

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
RootBeanDefinition pbd = new RootBeanDefinition();
MutablePropertyValues pValues = new MutablePropertyValues();
pValues.add("name", "小黃");
pbd.setBeanClass(Animal.class);
pbd.setPropertyValues(pValues);
GenericBeanDefinition cbd = new GenericBeanDefinition();
cbd.setBeanClass(Dog.class);
cbd.setParentName("parent");
MutablePropertyValues cValues = new MutablePropertyValues();
cValues.add("name", "小強");
cbd.setPropertyValues(cValues);
ctx.registerBeanDefinition("parent", pbd);
ctx.registerBeanDefinition("child", cbd);
ctx.refresh();
Dog child = (Dog) ctx.getBean("child");
System.out.println("child = " + child);

這里我使用了 RootBeanDefinition 來做 parent,其實從名字上就能看出來 RootBeanDefinition 適合做 parent,并且 RootBeanDefinition 不能作為 child。強行設(shè)置運行時會拋出異常,RootBeanDefinition#setParentName 方法如下:

@Override
public void setParentName(@Nullable String parentName) {
    if (parentName != null) {
        throw new IllegalArgumentException("Root bean cannot be changed into a child bean with parent reference");
    }
}

MutablePropertyValues 是為相應(yīng)的對象設(shè)置屬性值。

child 我這里使用了 GenericBeanDefinition,這個主要是做 child 的處理,最早有一個專門做 child 的 ChildBeanDefinition,不過自從 Spring2.5 開始提供了 GenericBeanDefinition 之后,現(xiàn)在用來做 child 首選 GenericBeanDefinition。

在上述案例中,parent 和 child 都設(shè)置了 name 屬性,那么 child 會覆蓋掉 parent,這一點和 Java 中的繼承一致。

用法就是這樣,并不難。

這就是 Spring BeanDefinition 中的父子關(guān)系問題。

2. 源碼分析

那么接下來我們也把這塊的源碼稍微來分析一下。

簡便起見,我們就不從 Bean 的創(chuàng)建開始分析了,直接來看和 BeanDefinition 中 parentName 屬性相關(guān)的地方,但是前面涉及到的方法還是給小伙伴們梳理一下,就是下圖:

那么這里涉及到的關(guān)鍵方法其實就是 AbstractBeanFactory#getMergedBeanDefinition:

protected RootBeanDefinition getMergedBeanDefinition(
        String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
        throws BeanDefinitionStoreException {
    synchronized (this.mergedBeanDefinitions) {
        RootBeanDefinition mbd = null;
        RootBeanDefinition previous = null;
        // Check with full lock now in order to enforce the same merged instance.
        if (containingBd == null) {
            mbd = this.mergedBeanDefinitions.get(beanName);
        }
        if (mbd == null || mbd.stale) {
            previous = mbd;
            if (bd.getParentName() == null) {
                // Use copy of given root bean definition.
                if (bd instanceof RootBeanDefinition rootBeanDef) {
                    mbd = rootBeanDef.cloneBeanDefinition();
                }
                else {
                    mbd = new RootBeanDefinition(bd);
                }
            }
            else {
                // Child bean definition: needs to be merged with parent.
                BeanDefinition pbd;
                try {
                    String parentBeanName = transformedBeanName(bd.getParentName());
                    if (!beanName.equals(parentBeanName)) {
                        pbd = getMergedBeanDefinition(parentBeanName);
                    }
                    else {
                        if (getParentBeanFactory() instanceof ConfigurableBeanFactory parent) {
                            pbd = parent.getMergedBeanDefinition(parentBeanName);
                        }
                        else {
                            throw new NoSuchBeanDefinitionException(parentBeanName,
                                    "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
                                            "': cannot be resolved without a ConfigurableBeanFactory parent");
                        }
                    }
                }
                // Deep copy with overridden values.
                mbd = new RootBeanDefinition(pbd);
                mbd.overrideFrom(bd);
            }
            // Set default singleton scope, if not configured before.
            if (!StringUtils.hasLength(mbd.getScope())) {
                mbd.setScope(SCOPE_SINGLETON);
            }
            // A bean contained in a non-singleton bean cannot be a singleton itself.
            // Let's correct this on the fly here, since this might be the result of
            // parent-child merging for the outer bean, in which case the original inner bean
            // definition will not have inherited the merged outer bean's singleton status.
            if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
                mbd.setScope(containingBd.getScope());
            }
            // Cache the merged bean definition for the time being
            // (it might still get re-merged later on in order to pick up metadata changes)
            if (containingBd == null && isCacheBeanMetadata()) {
                this.mergedBeanDefinitions.put(beanName, mbd);
            }
        }
        if (previous != null) {
            copyRelevantMergedBeanDefinitionCaches(previous, mbd);
        }
        return mbd;
    }
}

這個方法看名字就是要獲取一個合并之后的 BeanDefinition,就是將 child 中的屬性和 parent 中的屬性進行合并,然后返回,這個方法中有一個名為 mbd 的變量,這就是合并之后的結(jié)果。

  • 首先會嘗試從 mergedBeanDefinitions 變量中獲取到合并之后的 BeanDefinition,mergedBeanDefinitions 相當(dāng)于就是一個臨時緩存,如果之前已經(jīng)獲取過了,那么獲取成功之后就將之保存到 mergedBeanDefinitions 中,如果是第一次進入到該方法中,那么該變量中就沒有我們需要的數(shù)據(jù),所以會繼續(xù)執(zhí)行后面的步驟。
  • 當(dāng)?shù)?1 步并未拿到 mbd 的時候,接下來繼續(xù)判斷 bd.getParentName() 是否為空,這個其實就是查看當(dāng)前的 BeanDefinition 是否有設(shè)置 parentName,如果有設(shè)置,這里獲取到的就不為 null,否則為 null。如果這里獲取到的值為 null,那么就會根據(jù)當(dāng)前傳入的 BeanDefinition 生成一個 mbd,至于具體的生成方式:如果傳入的 BeanDefinition 是 RootBeanDefinition 類型的,則調(diào)用 clone 方法去生成 mbd(本質(zhì)上也是 new 一個新的 RootBeanDefinition),如果傳入的 BeanDefinition 不是 RootBeanDefinition 類型的,則直接 new 一個新的 RootBeanDefinition,在 new 的過程中,會把傳入的 BeanDefinition 上的屬性都復(fù)制到新的 RootBeanDefinition 中。
  • 如果 bd.getParentName() 不為空,則意味著存在 parent BeanDefinition,所以就要進行合并處理了,合并時候又有一個小細節(jié),如果 parentBeanName 等于當(dāng)前的 beanName,由于 Spring 在同一個容器中不允許存在同名的 bean,所以這就說明 parentBeanName 可能是父容器的 Bean,此時就要去父容器中去處理,當(dāng)然最終調(diào)用到的還是當(dāng)前方法,關(guān)于父子容器這一塊,小伙伴們可以參考之前的 Spring 中的父子容器是咋回事? 一文。如果 parentBeanName 不等于當(dāng)前 beanName,那么現(xiàn)在就可以調(diào)用 getMergedBeanDefinition 方法去獲取到 parentBeanDefinition 了,getMergedBeanDefinition 是當(dāng)前方法的重載方法,該方法最終也會調(diào)用到當(dāng)前方法,原因就在于 parentBeanDefinition 本身也可能存在 parentBeanDefinition。
  • 有了 pbd 之后,接下來 new 一個 RootBeanDefinition,然后調(diào)用 overrideFrom 方法進行屬性合并,合并的方式就是用傳入的 BeanDefinition 中的屬性去覆蓋 pbd 中同名的屬性。
  • 最后就是再設(shè)置 scope 屬性等,然后把 mbd 返回即可。

核心流程就是上面這個步驟,如此之后,拿到手的就是和 parent 合并之后的 BeanDefinition 了。

3. 小結(jié)

最后我們再來稍微總結(jié)下:

使用 parentName 屬性的一個主要優(yōu)勢是提高代碼的可維護性和重用性。當(dāng)我們需要創(chuàng)建多個相似的 bean 時,可以通過定義一個基礎(chǔ) bean,并在其他 bean 中使用 parentName 屬性來繼承其配置。這樣,我們只需在基礎(chǔ) bean 中定義一次配置,而不必為每個派生 bean 重復(fù)相同的配置。

另一個使用 parentName 屬性的場景是在多個層次結(jié)構(gòu)中定義 bean。假設(shè)我們有一個通用的基礎(chǔ)服務(wù)層 bean,而不同的業(yè)務(wù)模塊需要在此基礎(chǔ)上進行擴展。通過使用 parentName 屬性,我們可以為每個業(yè)務(wù)模塊定義一個派生 bean,并在其中添加特定于模塊的配置。這種層次結(jié)構(gòu)的定義使得我們可以更好地組織和管理不同模塊之間的 bean。

通過使用 parentName 屬性,我們可以輕松地創(chuàng)建和管理 bean 的層次結(jié)構(gòu)。這種繼承關(guān)系使得我們可以更好地組織和重用 bean 的配置,減少了代碼的冗余性。同時,它還提供了一種靈活的方式來定義不同模塊之間的 bean,使得應(yīng)用程序更易于擴展和維護。

綜上所述,Spring 框架中的 BeanDefinition 的 parentName 屬性允許我們在定義 bean 時建立父子關(guān)系,從而提高代碼的可維護性和重用性。通過繼承已有 bean 的配置,我們可以避免重復(fù)編寫相似的配置,并更好地組織和管理不同層次結(jié)構(gòu)的 bean。

有的小伙伴們可能會搞混今天內(nèi)容和之前松哥所寫的 Spring 父子容器之間的關(guān)系,小伙伴們參考這篇文章就清楚啦:Spring 中的父子容器是咋回事?。

另外,Spring BeanDefinition 中的 parentName 和 Java 中的繼承雖然有點像,但是并不能同等看待,它們之間也還是有一些區(qū)別的:

  • 概念和作用:Java 中的繼承是一種面向?qū)ο蟮木幊谈拍睿糜诙x類之間的父子關(guān)系,子類繼承父類的屬性和方法。而在 Spring 中,BeanDefinition 的 parentName 屬性是用于定義 bean 之間的父子關(guān)系,一個派生 bean 可以繼承另一個已定義的 bean 的配置。
  • 語法和用法:在 Java 中,繼承是通過使用關(guān)鍵字 extends 來實現(xiàn)的,子類通過繼承父類來獲得父類的屬性和方法。而在 Spring 中,通過在 BeanDefinition 中配置 parentName 屬性來指定一個 bean 的父 bean,從而繼承父 bean 的配置。
  • 范圍和應(yīng)用:Java 中的繼承主要用于類的繼承關(guān)系,用于定義類之間的層次結(jié)構(gòu)和代碼的重用。而在 Spring 中,BeanDefinition 的繼承主要用于定義 bean 之間的配置繼承關(guān)系,用于組織和管理 bean 的配置,提高代碼的可維護性和重用性。

好啦,Spring BeanDefinition 中的 parentName 屬性現(xiàn)在大家明白了吧~

以上就是Spring BeanDefinition父子關(guān)系示例解析的詳細內(nèi)容,更多關(guān)于Spring BeanDefinition父子關(guān)系的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java中Map和Set的常見用法舉例

    Java中Map和Set的常見用法舉例

    Map和Set是一種專門用來進行搜索的容器或者數(shù)據(jù)結(jié)構(gòu),其具體效率與具體的實例化子類有關(guān),下面這篇文章主要給大家介紹了關(guān)于Java中Map和Set的常見用法,需要的朋友可以參考下
    2024-04-04
  • 通過實例了解Java Integer類和int的區(qū)別

    通過實例了解Java Integer類和int的區(qū)別

    這篇文章主要介紹了通過實例了解Java Integer類和int的區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-03-03
  • mybatis?plus?MetaObjectHandler?不生效的解決

    mybatis?plus?MetaObjectHandler?不生效的解決

    今天使用mybatis-plus自動為更新和插入操作插入更新時間和插入時間,配置了MetaObjectHandler不生效,本文就來解決一下,具有一定的 參考價值,感興趣的可以了解一下
    2023-10-10
  • Java讀寫文件創(chuàng)建文件夾多種方法示例詳解

    Java讀寫文件創(chuàng)建文件夾多種方法示例詳解

    這篇文章主要介紹了Java讀寫文件創(chuàng)建文件夾等多種操作的方法,大家參考使用吧
    2013-11-11
  • Java游戲服務(wù)器系列之Netty相關(guān)知識總結(jié)

    Java游戲服務(wù)器系列之Netty相關(guān)知識總結(jié)

    今天帶大家來學(xué)習(xí)Java游戲服務(wù)器的相關(guān)知識,文中對Netty作了非常詳細的介紹,對正在學(xué)習(xí)java的小伙伴們有很好的幫助,需要的朋友可以參考下
    2021-05-05
  • 使用Okhttp實現(xiàn)上傳文件+參數(shù)請求接口form-data

    使用Okhttp實現(xiàn)上傳文件+參數(shù)請求接口form-data

    在進行接口對接時,常遇到需要傳遞多種類型參數(shù)及文件上傳的情況,解決此問題的關(guān)鍵在于參數(shù)傳遞和文件上傳的正確處理,在Service層和Controller層的傳參,可以通過@RequestParam標注或直接使用請求實體類,但若結(jié)合文件上傳,則不應(yīng)使用@RequestBody注解
    2024-10-10
  • Java class文件格式之方法_動力節(jié)點Java學(xué)院整理

    Java class文件格式之方法_動力節(jié)點Java學(xué)院整理

    這篇文章主要為大家詳細介紹了Java class文件格式之方法的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • SpringBoot+Redis使用AOP防止重復(fù)提交的實現(xiàn)

    SpringBoot+Redis使用AOP防止重復(fù)提交的實現(xiàn)

    本文主要介紹了SpringBoot+Redis使用AOP防止重復(fù)提交的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • Java精確計算BigDecimal類詳解

    Java精確計算BigDecimal類詳解

    這篇文章主要介紹了Java精確計算BigDecimal類的使用方法,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-12-12
  • Java進階之Object類及常用方法詳解

    Java進階之Object類及常用方法詳解

    Object?類是?Java?默認提供的一個類,是所有?Java?類的祖先類,每個類都使用?Object?作為父類。本文就來和大家聊聊Object類的常用方法,希望對大家有所幫助
    2023-01-01

最新評論