11-Spring初級(jí)容器初始化:BeanDefinition是什么呢?

開篇
上一節(jié),我們已經(jīng)分析到了bean標(biāo)簽具體解析的環(huán)節(jié)了,也初步解析了bean標(biāo)簽屬性的值,如id、name、class和parent,而且Spring還單獨(dú)創(chuàng)建了GenericBeanDefinition來存放這些屬性的值。
但是,我們可以確定以上的解析只是開始,在繼續(xù)分析源碼之前我們有必要來了解下GenericBeanDefinition,方便我們后續(xù)理解Spring其他各個(gè)環(huán)節(jié)的源碼,這一節(jié)主要包括以下幾個(gè)部分:
1.了解下BeanDefinition的類繼承體系,看下常見的都有哪些BeanDefinition實(shí)現(xiàn)類
2.再來簡單看下常見兩個(gè)BeanDefinition實(shí)現(xiàn)類都有什么樣的區(qū)別,為什么要這樣設(shè)計(jì)這些實(shí)現(xiàn)類
3.最后我們簡單看下BeanDefinition中有哪些東西,并沿著上節(jié)課的源碼分析,看下后續(xù)的解析流程是怎樣的
BeanDefinition的繼承體系
要了解GenericBeanDefinition,我們還得要先從BeanDefinition開始:

可以看到,BeanDefinition其實(shí)就是一個(gè)接口。
我們之前說過,在Spring容器中的每一個(gè)對(duì)象都稱為bean,每個(gè)bean在Spring容器中都是以BeanDefinition的形式存在的,BeanDefinition設(shè)計(jì)的初衷就是為了存放了bean的信息的,也就是bean的定義。
但是,我們也發(fā)現(xiàn)BeanDefinition終究是一個(gè)接口,有了接口就有對(duì)應(yīng)的實(shí)現(xiàn)類,那常見的BeanDefinition接口實(shí)現(xiàn)類的都有哪些呢?不同的BeanDefinition接口實(shí)現(xiàn)類都封裝了怎樣的bean信息呢?
我們結(jié)合BeanDefinition的類繼承圖來了解一下:

可以看到,直接實(shí)現(xiàn)BeanDefinition接口的為抽象類AbstractBeanDefinition,而繼承抽象類AbstractBeanDefinition的有三個(gè)類,分別是RootBeanDefinition、ChildrenBeanDefinition和GenericBeanDefinition,其中就有我們前面看到的GenericBeanDefinition。
其中,單個(gè)bean標(biāo)簽解析后在Spring容器中,起初是通過RootBeanDefinition來表示的,就像這樣:

因?yàn)閎ean標(biāo)簽中沒有設(shè)置parent屬性的值,也就是說它沒有指定自己的父bean,所以可以使用RootBeanDefinition來封裝該標(biāo)簽的信息,表示存放的是bean標(biāo)簽的根節(jié)點(diǎn)信息。
另外,我們可以看到在RootBeanDefinition的setParentName方法中,如果參數(shù)parentName不為空還會(huì)報(bào)錯(cuò):

很顯然,通過RootBeanDefinition來封裝bean的信息時(shí),就不允許再有“父親”了。
但是,如果bean標(biāo)簽中設(shè)置了parent屬性,就像這樣:

可以看到,id屬性值為child的bean標(biāo)簽,將parent屬性的值設(shè)置為student,也就是說它將id為student的bean標(biāo)簽作為自己的父標(biāo)簽,這個(gè)時(shí)候Spring就會(huì)將id為child的bean標(biāo)簽的信息,封裝在ChildBeanDefinition這個(gè)類中。
GenericBeanDefinition和AnnotatedGenericBeanDefinition
RootBeanDefinition和ChildBeanDefinition在早期的Spring版本中,大概也就是這樣來使用的,但是,隨著Spring后續(xù)版本的迭代,Spring更加的推薦我們使用GenericBeanDefinition:

可以看到,從Spring 2.5版本開始,Spring就推薦我們使用GenericBeanDefinition來替代RootBeanDefinition和ChildBeanDefinition,如果是子標(biāo)簽,GenericBeanDefinition也可以通過調(diào)用setParentName方法來設(shè)置parent屬性值。
GenericBeanDefinition包含了RootBeanDefinition和ChildBeanDefinition的所有公共屬性和方法,是一個(gè)更通用更一站式的BeanDefinition,上一節(jié)我們也看到,當(dāng)下Spring版本中創(chuàng)建的BeanDefinition就是GenericBeanDefinition類型了。
最后,我們?cè)倏吹絼偛诺念惱^承圖:

那AnnotatedGenericBeanDefinition是什么呢?其實(shí),RootBeanDefinition、ChildrenBeanDefinition和GenericBeanDefinition都是用來封裝從xml中解析來的bean信息,而AnnotatedGenericBeanDefinition從名稱上我們也可以看出一點(diǎn),那就是用來封裝和注解相關(guān)的bean。
也就是說,AnnotatedGenericBeanDefinition專門是用來封裝從注解掃描解析來的bean,比如@Bean、@Component、@Service、@Repository這些注解標(biāo)注的類,后面我們分析到注解那塊源碼再詳細(xì)來看下吧。
BeanDefinition中有哪些東西
現(xiàn)在我們已經(jīng)知道了BeanDefinition中都包含了bean的所有信息,也大概了解了一下BeanDefinition都有哪些實(shí)現(xiàn)類,那BeanDefinition中都有哪些東西呢,我們來看下:

可以看到,在BeanDefinition中,都只是一些屬性相關(guān)的方法而已。
因?yàn)槲覀儸F(xiàn)在已經(jīng)知道了,xml中的所有配置都是封裝在GenericBeanDefinition中的,但是,畢竟GenericBeanDefinition只是一個(gè)實(shí)現(xiàn)類,考慮到幾乎所有的BeanDefinition都繼承了抽象類AbstractBeanDefinition,一些通用的屬性還是保存在AbstractBeanDefinition中的。
我們?cè)俚紸bstractBeanDefinition類中看下:

可以看到,在AbstractBeanDefinition中果然封裝了很多公共的屬性。
不管我們是否認(rèn)識(shí)它們,我們現(xiàn)在心里都有一個(gè)把握了,那就是Spring容器中的每個(gè)bean中的信息都是封裝在BeanDefinition中的,其中AbstractBeanDefinition作為實(shí)現(xiàn)了BeanDefinition接口的抽象類,包含了xml以及注冊(cè)類型bean的大量公共屬性和方法。
但是,如果現(xiàn)在要把AbstractBeanDefinition中的每個(gè)屬性,都研究的非常透徹也是非常困難的,畢竟我們得要到具體的代碼邏輯中才能更好的理解,比如后面我們?cè)赽ean加載的環(huán)節(jié),這里面的各種屬性才會(huì)派上用場。
好了,接下來我們接著上一節(jié)的源碼流程,繼續(xù)來看下:

可以看到,上一節(jié)我們通過createBeanDefinition方法創(chuàng)建了GenericBeanDefinition對(duì)象。
接下來,我們接著看下parseBeanDefinitionAttributes方法在干什么,從方法名稱我們應(yīng)該可以猜到應(yīng)該就是要解析所有的屬性了:

可以看到,方法parseBeanDefinitionAttributes中果然在解析各種各樣的屬性,當(dāng)然我們并不需要死記硬背這些屬性,只需要知道都是在這個(gè)環(huán)節(jié)解析出來就行了,并且這些屬性和id、name、class和parent一樣都會(huì)封裝在GenericBeanDefinition中。
當(dāng)bean標(biāo)簽的各種屬性解析完了之后,接著就會(huì)開始解析bean標(biāo)簽下的各種子標(biāo)簽了:

可以看到,在這些子標(biāo)簽中,包括meta、lookup-method、replaced-method、constructor-args、property和qualifier,其中我們比較熟悉的還是標(biāo)簽constructor-args和property,下一節(jié),我們就來看下Spring是如何解析這些子標(biāo)簽元素的。
總結(jié)
好了,今天的知識(shí)點(diǎn),我們就講到這里了,我們來總結(jié)一下吧。
這一節(jié),我們主要來了解了下BeanDefinition接口體系大概都有哪些類,包括用來封裝xml中bean信息的RootBeanDefinition和ChildBeanDefinition,后續(xù)為了方便統(tǒng)一改用GenericBeanDefinition來封裝xml解析后的bean。
最后,我們還看了下BeanDefinition中包含各種各樣的屬性,也從Spring的源碼中看到各種各樣的屬性解析和封裝過程。
下一節(jié),我們看下Spring中常見的一些標(biāo)簽是如何解析的,到時(shí)候大家對(duì)于xml中,各種眼花繚亂的標(biāo)簽配置的處理,心里大概也就有個(gè)底了。