0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

初學(xué)者必看的SpringBoo自動(dòng)裝配原理2

jf_78858299 ? 來源:CSDN ? 作者:CC_且聽風(fēng)吟 ? 2023-04-07 11:03 ? 次閱讀

4. BeanDefinition結(jié)構(gòu)

既然講到了BeanDefinition,我們來看一下BeanDefinition里面究竟定義了些什么

讓我們點(diǎn)進(jìn)AbstractBeanDefinition這個(gè)類,一探究竟

哇!好多成員變量,整個(gè)人都要看暈了@_@

我們來重點(diǎn)關(guān)注以下三個(gè)成員:

private volatile Object beanClass;
private int autowireMode = AUTOWIRE_NO;
private ConstructorArgumentValues constructorArgumentValues;

4.1 beanClass

這個(gè)屬性決定了該Bean定義的真正class到底是誰,接下來我們來做點(diǎn)實(shí)驗(yàn)

我們定義兩個(gè)Bean類,A和B

@Component
public class A {
    @Value("我是AAA")
    private String name;
}
@Component
public class B {
    @Value("我是BBB")
    private String name;
}

接下來我們實(shí)現(xiàn)上面的BeanFactoryPostProcessor接口,來創(chuàng)建一個(gè)自定義的bean后置處理器

/**
 * 自定義的bean后置處理器
 * 通過這個(gè)MyBeanPostProcessor來修改bean定義的屬性
 * @author dzzhyk
 */
public class MyBeanPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        GenericBeanDefinition defA = (GenericBeanDefinition) beanFactory.getBeanDefinition("a");
        System.out.println("這里是MyBeanPostProcessor,我拿到了:" + defA.getBeanClassName());
    }
}

最后在XML配置文件中開啟包掃描

<context:component-scan base-package="pojo"/>
<context:annotation-config />

注意: 這里不要使用JavaConfig類來配置bean,不然會(huì)報(bào)如下錯(cuò)誤

ConfigurationClassBeanDefinition cannot be cast to org.springframework.beans.factory.support.GenericBeanDefinition

這個(gè)錯(cuò)誤出自這一句:

GenericBeanDefinition defA = (GenericBeanDefinition) beanFactory.getBeanDefinition("a");

最后,我們創(chuàng)建一個(gè)測試類:

public class Test {
    @org.junit.Test
    public void test(){
        ClassPathXmlApplicationContext ca = new ClassPathXmlApplicationContext("applicationContext.xml");
        A aaa = ca.getBean("a", A.class);
        System.out.println("最終拿到了==> " + aaa);
    }
}

測試運(yùn)行!

這里是MyBeanPostProcessor,我拿到了:pojo.A
最終拿到了==> A(name=我是AAA, b=B(name=我是BBB))

可以看到MyBeanPostProcessor成功拿到了A的Bean定義,并且輸出了提示信息

接下來讓我們做點(diǎn)壞事

我們?cè)贛yBeanPostProcessor中修改A的Bean對(duì)象,將A的beanClass修改為B.class

System.out.println("這里是MyBeanPostProcessor,我修改了:"+ defA.getBeanClassName() + " 的class為 B.class");
// 把A的class改成B
defA.setBeanClass(B.class);

重新運(yùn)行Test類,輸出了一些信息后:報(bào)錯(cuò)了!

這里是MyBeanPostProcessor,我拿到了:pojo.A
這里是MyBeanPostProcessor,我修改了:pojo.A 的class為 B.class

BeanNotOfRequiredTypeException: 
 Bean named 'a' is expected to be of type 'pojo.A' but was actually of type 'pojo.B'

我要拿到一個(gè)A類對(duì)象,你怎么給我一個(gè)B類對(duì)象呢?這明顯不對(duì)

綜上所述,我們可以得出beanClass屬性控制bean定義的類

4.2 autowireMode

我們繼續(xù)看第二個(gè)屬性:autowireMode,自動(dòng)裝配模式

我們?cè)贏bstractBeanDefinition源碼中可以看到:

private int autowireMode = AUTOWIRE_NO;

自動(dòng)裝配模式默認(rèn)是AUTOWIRE_NO,就是不開啟自動(dòng)裝配

可選的常量值有以下四種:不自動(dòng)裝配,通過名稱裝配,通過類型裝配,通過構(gòu)造器裝配

  • AUTOWIRE_NO
  • AUTOWIRE_BY_NAME
  • AUTOWIRE_BY_TYPE
  • AUTOWIRE_CONSTRUCTOR

接下來我們來模擬一個(gè)自動(dòng)裝配場景,仍然是A和B兩個(gè)類,現(xiàn)在在A類中添加B類對(duì)象

@Component
public class A {
    @Value("我是AAA")
    private String name;
    @Autowired
    private B b;
}

我們希望b對(duì)象能夠自動(dòng)裝配,于是我們給他加上了@Autowired注解,其他的完全不變,我們自定義的MyBeanPostProcessor中也不做任何操作,讓我們運(yùn)行測試類:

這里是MyBeanPostProcessor,我拿到了:pojo.A
最終拿到了==> A(name=我是AAA, b=B(name=我是BBB))

自動(dòng)裝配成功了!我們拿到的A類對(duì)象里面成功注入了B類對(duì)象b

現(xiàn)在問題來了,如果我把@Autowired注解去掉,自動(dòng)裝配會(huì)成功嗎?

這里是MyBeanPostProcessor,我拿到了:pojo.A
最終拿到了==> A(name=我是AAA, b=null)

必然是不成功的

但是我就是想要不加@Autowired注解,仍然可以實(shí)現(xiàn)自動(dòng)裝配,需要怎么做?

這時(shí)就要在我們的MyBeanPostProcessor中做文章了,加入如下內(nèi)容:

defA.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);

再輸出結(jié)果:

這里是MyBeanPostProcessor,我拿到了:pojo.A
最終拿到了==> A(name=我是AAA, b=B(name=我是BBB))

自動(dòng)裝配成功了!這次我們可沒加@Autowired,在我們的自定義的bean后置處理器中設(shè)置了autowireMode屬性,也實(shí)現(xiàn)了自動(dòng)裝配

綜上,autowireMode屬性是用來控制自動(dòng)裝配模式的,默認(rèn)值是AUTOWIRE_NO,即不自動(dòng)裝配

4.3 constructorArgumentValues

constructorArgumentValues的字面含義是構(gòu)造器參數(shù)

改變這個(gè)參數(shù)值,我們可以做到在實(shí)例化對(duì)象時(shí)指定特定的構(gòu)造器

話不多說,show me your code:

因?yàn)橐芯繕?gòu)造器,只能先”忍痛“關(guān)掉lombok插件,手寫一個(gè)pojo.Student類

/**
 * Student類
 * @author dzzhyk
 */
@Component
public class Student {
    private String name;
    private Integer age;

    public Student() {
        System.out.println("==>使用空參構(gòu)造器 Student()");
    }

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
        System.out.println("==>使用雙參數(shù)構(gòu)造器 Student(String name, Integer age)");
    }
}

我們都知道,spring在實(shí)例化對(duì)象時(shí)使用的是對(duì)象的默認(rèn)空參構(gòu)造器:

我們新建一個(gè)測試方法test

@Test
public void test(){
    ApplicationContext ca = new ClassPathXmlApplicationContext("applicationContext.xml");
    Student student = ca.getBean("stu", Student.class);
    System.out.println("==>" + student);
}

運(yùn)行可以得到下面結(jié)果:

這里是MyBeanPostProcessor,我拿到了:pojo.Student
==>使用空參構(gòu)造器 Student()
==>pojo.Student@402e37bc

可以看到,確實(shí)使用了空參構(gòu)造器

但是如何指定(自定義)使用哪個(gè)構(gòu)造器呢?我根本看不見摸不著,Spring全幫我做了,實(shí)在是太貼心了。

接下來就聊聊constructorArgumentValues的使用:

我們?cè)贛yBeanPostProcessor中加入如下內(nèi)容,對(duì)獲取到的pojo.Student的bean定義進(jìn)行操作:

ConstructorArgumentValues args = new ConstructorArgumentValues();
args.addIndexedArgumentValue(0, "我指定的姓名");
args.addIndexedArgumentValue(1, 20);
defStu.setConstructorArgumentValues(args);

再次運(yùn)行test:

這里是MyBeanPostProcessor,我拿到了:pojo.Student
==>使用雙參數(shù)構(gòu)造器 Student(String name, Integer age)
==>pojo.Student@2f177a4b

可以看到這次使用了雙參數(shù)構(gòu)造器

有人會(huì)好奇ConstructorArgumentValues到底是個(gè)什么東西,我點(diǎn)進(jìn)源碼研究一番,結(jié)果發(fā)現(xiàn)這個(gè)類就是一個(gè)普通的包裝類,包裝的對(duì)象是ValueHolder,里面一個(gè)List一個(gè)Map

而ValueHolder這個(gè)對(duì)象繼承于BeanMetadataElement,就是構(gòu)造器參數(shù)的一個(gè)包裝類型

通過這個(gè)例子我們可以看到ConstructorArgumentValues就是用來管控構(gòu)造器參數(shù)的,指定這個(gè)值會(huì)在進(jìn)行bean注入的時(shí)候選擇合適的構(gòu)造器。

5. 裝配對(duì)象

現(xiàn)在我們把目光放回到SpringBoot的自動(dòng)裝配上來,原來在真正進(jìn)行bean實(shí)例化對(duì)象前,我們前面還有這些過程,尤其是存在使用后置處理器BeanFactoryPostProcessor來對(duì)bean定義進(jìn)行各種自定義修改的操作。

經(jīng)過上面我們漫長的研究過程,我們終于可以回答第一個(gè)問題了:

自動(dòng)裝配的對(duì)象:Bean定義 (BeanDefinition)

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • spring
    +關(guān)注

    關(guān)注

    0

    文章

    335

    瀏覽量

    14278
  • 源碼分析
    +關(guān)注

    關(guān)注

    0

    文章

    5

    瀏覽量

    5537
  • 自動(dòng)裝配
    +關(guān)注

    關(guān)注

    0

    文章

    7

    瀏覽量

    641
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    protel技術(shù)大全--初學(xué)者必看

    protel技術(shù)大全--初學(xué)者必看。
    發(fā)表于 08-04 10:36

    PSOC1初學(xué)者必看的10個(gè)實(shí)例

    PSOC1初學(xué)者必看的10個(gè)實(shí)例
    發(fā)表于 11-19 16:33

    MATLAB入門教程-初學(xué)者必看

    MATLAB入門教程-初學(xué)者必看
    發(fā)表于 06-28 15:39

    Linux初學(xué)者必看!??!

    Linux初學(xué)者必看?。?!
    發(fā)表于 01-07 21:35

    單片機(jī)入門秘籍,初學(xué)者必看

    初學(xué)者必看的單片機(jī)秘籍
    發(fā)表于 04-29 16:10

    PCB LAYOUT初學(xué)者必看!

    PCBLAYOUT技術(shù)大全---初學(xué)者必看! PROTEL相關(guān)疑問 1.原理圖常見錯(cuò)誤: (1)ERC報(bào)告管腳沒有接入信號(hào): a.創(chuàng)建封裝時(shí)給管腳定義了I/O屬性; b.創(chuàng)建元件或放置元件時(shí)修改了不一致的grid屬性
    發(fā)表于 09-13 15:23 ?0次下載

    PSOC1初學(xué)者5個(gè)實(shí)驗(yàn),針對(duì)初學(xué)者的實(shí)驗(yàn)

    PSOC1初學(xué)者5個(gè)實(shí)驗(yàn),針對(duì)初學(xué)者的實(shí)驗(yàn)
    發(fā)表于 10-16 09:33 ?14次下載
    PSOC1<b class='flag-5'>初學(xué)者</b>5個(gè)實(shí)驗(yàn),針對(duì)<b class='flag-5'>初學(xué)者</b>的實(shí)驗(yàn)

    初學(xué)者必看的基本電子技術(shù)概念

    初學(xué)者必看的基本電子技術(shù)概念
    發(fā)表于 05-17 11:41 ?0次下載

    初學(xué)者必看的電源測試項(xiàng)目要點(diǎn)及教程

    初學(xué)者必看的電源測試項(xiàng)目要點(diǎn)及教程
    發(fā)表于 07-01 14:09 ?29次下載

    初學(xué)者必看的LABVIEW工程師編程經(jīng)驗(yàn)

    初學(xué)者必看的LABVIEW工程師編程經(jīng)驗(yàn)
    發(fā)表于 07-12 14:24 ?29次下載

    ARM與嵌入式linux入門的建議(初學(xué)者必看)

    ARM與嵌入式linux入門的建議(初學(xué)者必看)(嵌入式開發(fā)培訓(xùn)怎么樣)-該文檔為ARM與嵌入式linux入門的建議(初學(xué)者必看)總結(jié)文檔,是一份很不錯(cuò)的參考資料,具有較高參考價(jià)值,感
    發(fā)表于 08-04 10:02 ?15次下載
    ARM與嵌入式linux入門的建議(<b class='flag-5'>初學(xué)者</b><b class='flag-5'>必看</b>)

    初學(xué)者必看的單片機(jī)程序匯總

    初學(xué)者必看的單片機(jī)程序匯總
    發(fā)表于 09-15 14:33 ?49次下載

    初學(xué)者必看SpringBoo自動(dòng)裝配原理1

    學(xué)習(xí)SpringBoot,絕對(duì)避不開自動(dòng)裝配這個(gè)概念,這也是SpringBoot的關(guān)鍵之一 本人也是SpringBoot的初學(xué)者,下面的一些總結(jié)都是結(jié)合個(gè)人理解和實(shí)踐得出的,如果有錯(cuò)誤或者疏漏,請(qǐng)一定一定一定(不是歡迎,是
    的頭像 發(fā)表于 04-07 11:03 ?582次閱讀
    <b class='flag-5'>初學(xué)者</b><b class='flag-5'>必看</b>的<b class='flag-5'>SpringBoo</b><b class='flag-5'>自動(dòng)</b><b class='flag-5'>裝配</b>原理1

    初學(xué)者必看SpringBoo自動(dòng)裝配原理3

    學(xué)習(xí)SpringBoot,絕對(duì)避不開自動(dòng)裝配這個(gè)概念,這也是SpringBoot的關(guān)鍵之一 本人也是SpringBoot的初學(xué)者,下面的一些總結(jié)都是結(jié)合個(gè)人理解和實(shí)踐得出的,如果有錯(cuò)誤或者疏漏,請(qǐng)一定一定一定(不是歡迎,是
    的頭像 發(fā)表于 04-07 11:03 ?512次閱讀
    <b class='flag-5'>初學(xué)者</b><b class='flag-5'>必看</b>的<b class='flag-5'>SpringBoo</b><b class='flag-5'>自動(dòng)</b><b class='flag-5'>裝配</b>原理3

    初學(xué)者必看SpringBoo自動(dòng)裝配原理4

    學(xué)習(xí)SpringBoot,絕對(duì)避不開自動(dòng)裝配這個(gè)概念,這也是SpringBoot的關(guān)鍵之一 本人也是SpringBoot的初學(xué)者,下面的一些總結(jié)都是結(jié)合個(gè)人理解和實(shí)踐得出的,如果有錯(cuò)誤或者疏漏,請(qǐng)一定一定一定(不是歡迎,是
    的頭像 發(fā)表于 04-07 11:03 ?590次閱讀
    <b class='flag-5'>初學(xué)者</b><b class='flag-5'>必看</b>的<b class='flag-5'>SpringBoo</b><b class='flag-5'>自動(dòng)</b><b class='flag-5'>裝配</b>原理4