本文是自己學習的一個總結
目錄
1、什麼是BeanDefinition
BeanDefinition是Spring Framework中定義Bean的配置原信息接口,其中包含
- Bean的類名。它是一個全限定性名稱。Bean是一個實例對象,Bean的類名就是指Bean的類型。
- Bean的行爲配置元素,如作用域,自動綁定的模式,生命週期回調等。
- 其他Bean引用,又稱爲合作者(Collaborators)或者依賴(Dependencies)。合作者其實就是依賴關係,這裏不用太在意。
- 配置設置,比如Bean的屬性。
1.1、BeanFactory中的BeanDefinition
BeanFactory獲取對象的方式是這樣的
//container是已經定義的一個容器,BeanFactory類型
ClassA a = container.getBean("a");
雖然BeanFactory會自動維護對象,但我們得先讓容器知道哪些對象需要維護,這裏就涉及到BeanFactory的對象註冊和依賴綁定方式。
Spring對象註冊和依賴綁定主要有直接編碼方式、外部文件配置方式(這裏不記錄具體的綁定方式,只是記錄依賴綁定的類BeanDefinitionRegistry和其他相關類)。
前面我們知道,BeanFactory內提供了獲取容器中管理Bean對象的方法和一些獲取Bean對象的屬性的方法,但Bean被容器管理之前要先被註冊,有依賴的建立依賴,而這些服務的提供者不是BeanFactory而是BeanDefinition。BeanDefinition和BeanFactory的關係圖如下。
如果我們直接使用編碼方式來綁定依賴的話,會使用同時繼承BeanFactory和BeanDefinitionRegistry的DefaultListableBeanFactory來完成註冊,然後將註冊號的DefaultListableBeanFactory當做BeanFactory類型返回(這樣的好處就是外界就只能調用作爲BeanFactory類型的返回對象中,關於獲取Bean的服務)。
下面的代碼展示了這一過程,這段代碼演示了使用DefaultListableBeanFactory註冊幾個Bean(註冊Provider,Listener和Persister,其中Provider關聯Listener和Persister,即Provider的構造函數的參數類型是Listener和Persiter),然後再從DefaultListableBeanFactory容器中取出其中一個Bean對象。
public static void main(String[] args) {
DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
BeanFactory container = (BeanFactory) bindViaCode(beanRegistry);
Provider a = container.getBean("new provider");
}
BeanFactory bindViaCode(DefaultListableBeanFactory registry) {
//將三個類放到BeanDefinition的實現類AbstractBeanDefinition中,等會再將他們註冊到registry中
AbstractBeanDefinition newProvider = new RootBeanDefinition(Provider.class, true);
AbstractBeanDefinition newListener = new RootBeanDefinition(Listener.class, true);
AbstractBeanDefinition newPersister = new RootBeanDefinition(Persister.class, true);
//將三個類註冊到容器中,還沒完,因爲Provider的構造函數需要另外兩個類對象
registry.registerBeanDefinition("new provider", newProvider);
registry.registerBeanDefinition("new listener", newListener);
registry.registerBeanDefinition("new persister", newPersister);
//構造函數的注入依賴方式
ConstructiorArgumentValues argValues = new ConstructorArgument.Values();
argValues.addIndexedArgumentValue(0, newListener);
argValues.addIndexedArgumentValue(0, newListener);
newProvider.setConstructorArgumentValues(argValues);
//已BeanFactory的類型返回,這樣客戶端就只能調用獲取Bean的方法,不能再新註冊Bean到容器中,有利於封裝。
return (BeanFactory) registry;
}
當然,現在寫工程項目很少會用到代碼手動註冊的方式,註解@Autowired會用的比較多,這裏只是爲了展示DefaultListableBeanFactory、BeanDefinition、BeanFactory的關係才貼上這些代碼。
2、BeanDefinition中的元信息
屬性 | 說明 |
---|---|
beanClass | Bean的全類名,必須是具體類,不能是抽象類或接口。因爲Bean是實例對象,抽象類或接口是不能實例的,不能作爲Bean的類名。 |
name | Bean的名稱或者id |
scope | Bean的作用域,比如默認的單例模式,還有原生模式。 |
constructorArgumentValues | Bean的構造器參數,用於依賴注入。 |
propertiesValues | Bean中成員屬性 |
autowireMode | 自動綁定的模式,比如byName,通過名稱自動綁定;byType,通過類型自動綁定等。這是可以設置的。 |
lazyInit | boolean值,是否延遲初始化。默認是非延遲初始化。延遲化的好處是可以有效地減少啓動時間。 |
initMethodName | Bean初始化回調方法名稱 |
destroyMethodName | Bean銷燬回調方法名稱 |
3、BeanDefinition的構建
BeanDefinition的構建主要有兩種方法。
- 通過BeanDefinitionBuilder。
- 通過AbstractBeanDefinition以及派生類。
3.1、BeanDefinitionBuilder構建BeanDefinition
3.1.1 childBeanDefinition、rootBeanDefinition和genericBeanDefinition的區別
BeanDefinitionBuilder內部有許多靜態方法構建BeanDefinition。這些靜態方法分爲以下三種,所有的靜態方法這三個方法的重載形式。
- childBeanDefinition()
- rootBeanDefinition()
- genericBeanDefinition()
三者從名字上其實就可以看出區別。雖然三個方法返回的都是BeanDefinitionBuilder,但是三者其實是和三個BeanDefinition相關。rootBeanDefinition相關的是RootBeanDefinition。RootBeanDefinition和ChildBeanDefinition是相互配合的,ChildBeanDefinition可以直接繼承RootBeanDefinition中的元信息。比如下面的代碼。
//假設容器中已定義一個RootBeanDefinition命名爲rootBeanDefinition。
//下面這段語句就令childBeanDefinition直接快速繼承rootBeanDefinition中的元信息。
//GenericBeanDefinition就沒辦法這樣快速繼承。
ChildBeanDefinition childBeanDefinition = new ChildBeanDefinition("rootBeanDefinition");
GenericBeanDefinition就相關一個普通的GenericBeanDefinition。三者其實都是一樣的,只是GenericBeanDefinition沒法像ChildBeanDefinition和RootBeanDefinition一樣相互聯動。
BeanDefinitionBuilder構建BeanDefinition的方式
下面我們使用簡單示例,使用genericBeanDefintion構建簡單的BeanDefinition。
我們想構建一個描述User類的BeanDefinition。User類的描述如下。
public class User {
private Long id;
private String name;
//省略setter和getter
}
下面開始通過BeanDefinitionBuilder構建BeanDefinition
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
//設置Bean的屬性
beanDefinitionBuilder.addPropertyValue("name", "用戶1");
beanDefinitionBuilder.addPropertyValue("id", 10);
//獲取到BeanDefintion
BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
打開斷點可以看到beanDefinition的信息如下。
3.2 通過AbstractBeanDefinition及其派生類構建BeanDefinition
這種方式相較於上一種就比較直接,我們不借用BeanDefinitonBuilder這個中間工具來構建BeanDefinition,而是直接創建一個BeanDefinition,然後直接設置元信息。
AbstractBeanDefintion的子類中最常見和通用的就是GenericBeanDefintion,下面舉例也是使用GenericBeanDefintion。
//直接new一個GenericBeanDefinition
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
//然後設置要描述的類
genericBeanDefinition.setBeanClass(User.class);
//設置類的Property,不能像BeanDefinitionBuilder一樣直接設置,要通過MutablePropertyValues這個中間人
MutablePropertyValues();
propertyValues.addPropertyValue("name", "用戶1");
propertyValues.addPropertyValue("id", 10); //也可以寫成propertyValues.add("id", 1).add("name", "用戶1");
genericBeanDefinition.setPropertyValues(propertyValues);
4、將BeanDefinition註冊到容器中
回到這張圖,將BeanDefinition定義好之後,還要將其註冊到容器中
將BeanDefinition註冊到容器中有兩種種方式
- AnnotationConfigApplicationContext#register(Class class);
- BeanDefinitionRegistry#registerBeanDefinition(String beanName, BeanDefinition beanDefinition);