4. BeanDefinition結(jié)構(gòu)
既然講到了BeanDefinition,我們來(lái)看一下BeanDefinition里面究竟定義了些什么
讓我們點(diǎn)進(jìn)AbstractBeanDefinition這個(gè)類,一探究竟
哇!好多成員變量,整個(gè)人都要看暈了@_@
我們來(lái)重點(diǎn)關(guān)注以下三個(gè)成員:
private volatile Object beanClass;
private int autowireMode = AUTOWIRE_NO;
private ConstructorArgumentValues constructorArgumentValues;
4.1 beanClass
這個(gè)屬性決定了該Bean定義的真正class到底是誰(shuí),接下來(lái)我們來(lái)做點(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;
}
接下來(lái)我們實(shí)現(xiàn)上面的BeanFactoryPostProcessor接口,來(lái)創(chuàng)建一個(gè)自定義的bean后置處理器
/**
* 自定義的bean后置處理器
* 通過(guò)這個(gè)MyBeanPostProcessor來(lái)修改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類來(lái)配置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è)測(cè)試類:
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);
}
}
測(cè)試運(yùn)行!
這里是MyBeanPostProcessor,我拿到了:pojo.A
最終拿到了==> A(name=我是AAA, b=B(name=我是BBB))
可以看到MyBeanPostProcessor成功拿到了A的Bean定義,并且輸出了提示信息
接下來(lái)讓我們做點(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)裝配,通過(guò)名稱裝配,通過(guò)類型裝配,通過(guò)構(gòu)造器裝配
- AUTOWIRE_NO
- AUTOWIRE_BY_NAME
- AUTOWIRE_BY_TYPE
- AUTOWIRE_CONSTRUCTOR
接下來(lái)我們來(lái)模擬一個(gè)自動(dòng)裝配場(chǎ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)行測(cè)試類:
這里是MyBeanPostProcessor,我拿到了:pojo.A
最終拿到了==> A(name=我是AAA, b=B(name=我是BBB))
自動(dòng)裝配成功了!我們拿到的A類對(duì)象里面成功注入了B類對(duì)象b
現(xiàn)在問(wèn)題來(lái)了,如果我把@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)裝配成功了!這次我們可沒(méi)加@Autowired,在我們的自定義的bean后置處理器中設(shè)置了autowireMode屬性,也實(shí)現(xiàn)了自動(dòng)裝配
綜上,autowireMode屬性是用來(lái)控制自動(dòng)裝配模式的,默認(rèn)值是AUTOWIRE_NO,即不自動(dòng)裝配
4.3 constructorArgumentValues
constructorArgumentValues的字面含義是構(gòu)造器參數(shù)值
改變這個(gè)參數(shù)值,我們可以做到在實(shí)例化對(duì)象時(shí)指定特定的構(gòu)造器
話不多說(shuō),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è)測(cè)試方法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í)在是太貼心了。
接下來(lái)就聊聊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è)包裝類型
通過(guò)這個(gè)例子我們可以看到ConstructorArgumentValues就是用來(lái)管控構(gòu)造器參數(shù)的,指定這個(gè)值會(huì)在進(jìn)行bean注入的時(shí)候選擇合適的構(gòu)造器。
5. 裝配對(duì)象
現(xiàn)在我們把目光放回到SpringBoot的自動(dòng)裝配上來(lái),原來(lái)在真正進(jìn)行bean實(shí)例化對(duì)象前,我們前面還有這些過(guò)程,尤其是存在使用后置處理器BeanFactoryPostProcessor來(lái)對(duì)bean定義進(jìn)行各種自定義修改的操作。
經(jīng)過(guò)上面我們漫長(zhǎng)的研究過(guò)程,我們終于可以回答第一個(gè)問(wèn)題了:
自動(dòng)裝配的對(duì)象:Bean定義 (BeanDefinition)
-
spring
+關(guān)注
關(guān)注
0文章
340瀏覽量
14362 -
源碼分析
+關(guān)注
關(guān)注
0文章
5瀏覽量
5565 -
自動(dòng)裝配
+關(guān)注
關(guān)注
0文章
7瀏覽量
667
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論