Spring學習筆記:BeanDefinition

本文是自己學習的一個總結


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);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章