4. BeanDefinition結構
既然講到了BeanDefinition,我們來看一下BeanDefinition里面究竟定義了些什么
讓我們點進AbstractBeanDefinition這個類,一探究竟
哇!好多成員變量,整個人都要看暈了@_@
我們來重點關注以下三個成員:
private volatile Object beanClass;
private int autowireMode = AUTOWIRE_NO;
private ConstructorArgumentValues constructorArgumentValues;
4.1 beanClass
這個屬性決定了該Bean定義的真正class到底是誰,接下來我們來做點實驗
我們定義兩個Bean類,A和B
@Component
public class A {
@Value("我是AAA")
private String name;
}
@Component
public class B {
@Value("我是BBB")
private String name;
}
接下來我們實現上面的BeanFactoryPostProcessor接口,來創建一個自定義的bean后置處理器
/**
* 自定義的bean后置處理器
* 通過這個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,不然會報如下錯誤
ConfigurationClassBeanDefinition cannot be cast to org.springframework.beans.factory.support.GenericBeanDefinition
這個錯誤出自這一句:
GenericBeanDefinition defA = (GenericBeanDefinition) beanFactory.getBeanDefinition("a");
最后,我們創建一個測試類:
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);
}
}
測試運行!
這里是MyBeanPostProcessor,我拿到了:pojo.A
最終拿到了==> A(name=我是AAA, b=B(name=我是BBB))
可以看到MyBeanPostProcessor成功拿到了A的Bean定義,并且輸出了提示信息
接下來讓我們做點壞事
我們在MyBeanPostProcessor中修改A的Bean對象,將A的beanClass修改為B.class
System.out.println("這里是MyBeanPostProcessor,我修改了:"+ defA.getBeanClassName() + " 的class為 B.class");
// 把A的class改成B
defA.setBeanClass(B.class);
重新運行Test類,輸出了一些信息后:報錯了!
這里是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'
我要拿到一個A類對象,你怎么給我一個B類對象呢?這明顯不對
綜上所述,我們可以得出beanClass屬性控制bean定義的類
4.2 autowireMode
我們繼續看第二個屬性:autowireMode,自動裝配模式
我們在AbstractBeanDefinition源碼中可以看到:
private int autowireMode = AUTOWIRE_NO;
自動裝配模式默認是AUTOWIRE_NO,就是不開啟自動裝配
可選的常量值有以下四種:不自動裝配,通過名稱裝配,通過類型裝配,通過構造器裝配
- AUTOWIRE_NO
- AUTOWIRE_BY_NAME
- AUTOWIRE_BY_TYPE
- AUTOWIRE_CONSTRUCTOR
接下來我們來模擬一個自動裝配場景,仍然是A和B兩個類,現在在A類中添加B類對象
@Component
public class A {
@Value("我是AAA")
private String name;
@Autowired
private B b;
}
我們希望b對象能夠自動裝配,于是我們給他加上了@Autowired注解,其他的完全不變,我們自定義的MyBeanPostProcessor中也不做任何操作,讓我們運行測試類:
這里是MyBeanPostProcessor,我拿到了:pojo.A
最終拿到了==> A(name=我是AAA, b=B(name=我是BBB))
自動裝配成功了!我們拿到的A類對象里面成功注入了B類對象b
現在問題來了,如果我把@Autowired注解去掉,自動裝配會成功嗎?
這里是MyBeanPostProcessor,我拿到了:pojo.A
最終拿到了==> A(name=我是AAA, b=null)
必然是不成功的
但是我就是想要不加@Autowired注解,仍然可以實現自動裝配,需要怎么做?
這時就要在我們的MyBeanPostProcessor中做文章了,加入如下內容:
defA.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
再輸出結果:
這里是MyBeanPostProcessor,我拿到了:pojo.A
最終拿到了==> A(name=我是AAA, b=B(name=我是BBB))
自動裝配成功了!這次我們可沒加@Autowired,在我們的自定義的bean后置處理器中設置了autowireMode屬性,也實現了自動裝配
綜上,autowireMode屬性是用來控制自動裝配模式的,默認值是AUTOWIRE_NO,即不自動裝配
4.3 constructorArgumentValues
constructorArgumentValues的字面含義是構造器參數值
改變這個參數值,我們可以做到在實例化對象時指定特定的構造器
話不多說,show me your code:
因為要研究構造器,只能先”忍痛“關掉lombok插件,手寫一個pojo.Student類
/**
* Student類
* @author dzzhyk
*/
@Component
public class Student {
private String name;
private Integer age;
public Student() {
System.out.println("==>使用空參構造器 Student()");
}
public Student(String name, Integer age) {
this.name = name;
this.age = age;
System.out.println("==>使用雙參數構造器 Student(String name, Integer age)");
}
}
我們都知道,spring在實例化對象時使用的是對象的默認空參構造器:
我們新建一個測試方法test
@Test
public void test(){
ApplicationContext ca = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = ca.getBean("stu", Student.class);
System.out.println("==>" + student);
}
運行可以得到下面結果:
這里是MyBeanPostProcessor,我拿到了:pojo.Student
==>使用空參構造器 Student()
==>pojo.Student@402e37bc
可以看到,確實使用了空參構造器
但是如何指定(自定義)使用哪個構造器呢?我根本看不見摸不著,Spring全幫我做了,實在是太貼心了。
接下來就聊聊constructorArgumentValues的使用:
我們在MyBeanPostProcessor中加入如下內容,對獲取到的pojo.Student的bean定義進行操作:
ConstructorArgumentValues args = new ConstructorArgumentValues();
args.addIndexedArgumentValue(0, "我指定的姓名");
args.addIndexedArgumentValue(1, 20);
defStu.setConstructorArgumentValues(args);
再次運行test:
這里是MyBeanPostProcessor,我拿到了:pojo.Student
==>使用雙參數構造器 Student(String name, Integer age)
==>pojo.Student@2f177a4b
可以看到這次使用了雙參數構造器
有人會好奇ConstructorArgumentValues到底是個什么東西,我點進源碼研究一番,結果發現這個類就是一個普通的包裝類,包裝的對象是ValueHolder,里面一個List一個Map
而ValueHolder這個對象繼承于BeanMetadataElement,就是構造器參數的一個包裝類型
通過這個例子我們可以看到ConstructorArgumentValues就是用來管控構造器參數的,指定這個值會在進行bean注入的時候選擇合適的構造器。
5. 裝配對象
現在我們把目光放回到SpringBoot的自動裝配上來,原來在真正進行bean實例化對象前,我們前面還有這些過程,尤其是存在使用后置處理器BeanFactoryPostProcessor來對bean定義進行各種自定義修改的操作。
經過上面我們漫長的研究過程,我們終于可以回答第一個問題了:
自動裝配的對象:Bean定義 (BeanDefinition)
-
spring
+關注
關注
0文章
340瀏覽量
14338 -
源碼分析
+關注
關注
0文章
5瀏覽量
5546 -
自動裝配
+關注
關注
0文章
7瀏覽量
649
發布評論請先 登錄
相關推薦
評論