BeanFactory
Spring Ioc
是一個(gè)管理Bean的容器,在Spring的定義中,他要求所有的Ioc容器都需要實(shí)現(xiàn)接口BeanFactory
字支。==BeanFactory
是一個(gè)頂級容器接口。==
public interface BeanFactory {
// 前綴
String FACTORY_BEAN_PREFIX = "&";
// 根據(jù)名稱獲取bean
Object getBean(String name) throws BeansException;
// 根據(jù)名稱獲取bean,返回指定類型
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
// 根據(jù)名稱獲取bean梨撞,使用指定的參數(shù)初始化
Object getBean(String name, Object... args) throws BeansException;
// 根據(jù)類型獲取bean
<T> T getBean(Class<T> requiredType) throws BeansException;
// 根據(jù)類型獲取bean,使用指定的參數(shù)初始化
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
// 根據(jù)類型獲取bean提供者
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
// 是否存在指定名稱的bean
boolean containsBean(String name);
// 指定的bean是否是單例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
// 指定的bean是否是原型
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
// 是否類型匹配
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
// 獲取指定bean的類型
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
// 獲取bean的別名
String[] getAliases(String name);
}
在BeanFactory
中含有多個(gè)getBean方法香罐,這是Ioc容器最重要的方法之一卧波,主要是從Ioc容器中獲取Bean。這些getBean的方法中有按類型庇茫、按名稱獲取bean港粱,這也是說,在Spring Ioc容器中旦签,允許按照名稱或者類型獲取bean.
由于BeanFacttory
的功能還不夠強(qiáng)大查坪,因此Spring在BeanFactory
的基礎(chǔ)上,還設(shè)計(jì)了更為高級的接口:ApplicationContext
宁炫,這是BeanFactory
的子接口之一,在Spring的體系中BeanFactory
和ApplicationContext
是最為重要的接口設(shè)計(jì)偿曙,實(shí)際使用的大部分Spring Ioc容器是ApplicationContext
接口的實(shí)現(xiàn)類。
ApplicationContext
接口通過集成上級接口羔巢,進(jìn)而集成BeanFactory
接口望忆。在BeanFactory
的基礎(chǔ)上,擴(kuò)展了消息國際化接口(MessageSource)和資源模式解析接口(ResourcePatternResolver)竿秆。
容器的基本使用
我們在idea中創(chuàng)建一個(gè)springboot的工程启摄,只需要lombok和web的starter即可。(gradle)
我們這里創(chuàng)建了spring boot的工程幽钢,但是只是使用gradle自動(dòng)下載spring的依賴即可歉备。
還記得一個(gè)spring項(xiàng)目是如何創(chuàng)建的嗎?
復(fù)習(xí)一下:
spring--hello~!(如何搭建一個(gè)spring項(xiàng)目)
省去依賴包搅吁,我們創(chuàng)建一個(gè)spring的項(xiàng)目
新增bean
新增beans.xml
然后注釋掉spring boot中原來的代碼威创,添加我們的代碼
運(yùn)行
可以看到落午,最終,我們打印出了people的name.也就是說肚豺,通過XmlBeanFactory
加載了我們制定的配置文件后溃斋,使用指定的配置文件,創(chuàng)建了我們需要的Ioc容器吸申。最后我們可以通過名字獲取bean梗劫。
XmlBeanFactory源碼
雖然XmlBeanFactory
基本上沒有人在使用,而且也被標(biāo)記為廢棄了截碴,但是梳侨,作為我spring入門的一個(gè)方法,我還是想看看其具體的實(shí)現(xiàn)日丹。
這是XmlBeanFactory
的UML圖:
我們可以看到走哺,XmlBeanFactory
直接繼承DefaultListableBeanFactory
,而DefaultListableBeanFactory
上面又有非常多的繼承關(guān)系。
-
AliasRegistry
:定義對alias的簡單增刪改等操作哲虾。 -
SimpleAliasRegistry
:主要使用Map作為alias的緩存丙躏,并對接口AliasRegistry
進(jìn)行實(shí)現(xiàn)。 -
SingletonBeanRegistry
:定義對單例的注冊及獲取束凑。 -
BeanFactry
:定義獲取bean及bean的各種特性晒旅。 -
DefaultSingletonBeanRegistry
:對接口SingletonBeanRegistry
的實(shí)現(xiàn)。 -
HierarchicalBeanFactory
:繼承BeanFactory
汪诉,也就是在BeanFactory
定義的功能的基礎(chǔ)上增加了對parentFactory
的支持废恋。 -
BeanDefinitionRegistry
:定義對BeanDefinition
的各種增刪改操作。 -
FactoryBeanRegistrySupport
:在DefaultSingletonBeanRegistry
基礎(chǔ)上增加了對BeanFactory
的特殊處理扒寄。 -
ConfigurableBeanFactory
:提供配置Factory的各種方法鱼鼓。 -
ListableBeanFactory
:根據(jù)各種條件獲取bean的配置清單。 -
AbstractBeanFactory
:綜合FactoryBeanRegistrySupport
和ConfigurableBeanFactory
的功能该编。 -
AutowireCapableBeanFactory
:提供創(chuàng)建bean蚓哩、自動(dòng)注入、初始化以及應(yīng)用bean的后處理器上渴。 -
AbstractAutowireCapableBeanFactory
A:綜合AbstractBeanFactory
并對接口AutowireCapableBeanFactory
進(jìn)行實(shí)現(xiàn)。 -
ConfigurableListableBeanFactory
:BeanFactory
配置清單喜颁,指定忽略類型及接口等稠氮。 -
DefaultListableBeanFactory
:綜合上述全部功能,主要是對Bean進(jìn)行注冊后的處理半开。
XmlBeanFactory
對DefaultListableBeanFactory
進(jìn)行了擴(kuò)展隔披,從Xml文檔中讀取BeanDefinition
,對于注冊和獲取都是使用的父類繼承的方法寂拆。
XmlBeanDefinitionReader
這是XmlBeanDefinitionReader
的UML圖
這些類主要的操作:
-
ResourceLoader
:定義資源加載器奢米,主要應(yīng)用于根據(jù)給定的資源文件地址返回對應(yīng)的Resource抓韩。 -
BeanDefinitionReader
:主要定義自語言文件讀取并轉(zhuǎn)換為BeanDefinition
的各個(gè)功能。 -
EnvironmentCapable
:定義獲取Environment方法鬓长。 -
DockumentLoader
:定義從資源文件加載到轉(zhuǎn)換為Document的功能谒拴。 -
AbstractBeanDefinitionReader
:對EnvironmentCapable,BeanDefinitionReader
類定義的功能進(jìn)行實(shí)現(xiàn)。 -
BeanDefinitionDocumentReader
定義讀取Document并注冊BeanDefinition功能涉波。 -
BeanDefinitionParserDelegate
:定義解析Element的各種方法英上。
XmlBeanFactory
我們看下XmlBeanFactory
的時(shí)序圖:
在main方法中首先調(diào)用ClassPathResource
的構(gòu)造方法來構(gòu)造Resource資源文件的實(shí)例對象,這樣后續(xù)的資源處理就可以用Resource提供的服務(wù)進(jìn)行操作啤覆,有了Resource后苍日,就可以進(jìn)行XmlBeanFactory
的初始化了。
配置文件的封裝
Spring的配置文件讀取是通過ClassPathResource
進(jìn)行封裝的窗声,比如new ClassPathResource("beans.xml")
相恃。
在java中,將不同來源的資源抽象成URL笨觅,通過注冊不同的handler(URLStreamHandler)
來處理不同來源的資源的讀取邏輯拦耐,一般handler的類型使用不同的前綴來識別,比如:file:,http:,jar:
等屋摇。但是URL沒有默認(rèn)定義相對ClassPath或者ServletContext等資源的handler揩魂,雖然可以注冊自己的URLStreamHandler來解析特定的URL前綴(比如cclasspath:
),這樣需要了解URL的實(shí)現(xiàn)機(jī)制,而且URL也沒有提供基本方法檢查資源是否存在等炮温。所以Spring對內(nèi)部使用到的資源資源實(shí)現(xiàn)了自己的抽象結(jié)構(gòu):Resource
這是Resource的方法
Resource接口抽象了所有Spring內(nèi)部使用到的底層資源:File,URL,Classpath等火脉。
定義了3個(gè)判斷當(dāng)前資源狀態(tài)的方法:存在性(exists
)、可讀性(isReadable
)柒啤、是否處于打開狀態(tài)(isOpen
)倦挂。另外,Resource接口還提供了不同資源到URL担巩、URI方援、File類型的轉(zhuǎn)換,以及獲取lastModified
屬性涛癌、文件名(不帶路徑信息的文件名犯戏,getFilename()
)的方法。為了便于操作拳话,Resource 還提供了基于當(dāng)前資源創(chuàng)建一個(gè)相對資源的方法:createRelativeO
先匪。在錯(cuò)誤處理中需要詳細(xì)地打印出錯(cuò)的資源文件,因而 Resource
還提供了 getDescription
()方法用于在錯(cuò)誤處理中的打印信息弃衍。
不同來源的資源文件都有相應(yīng)的Resource實(shí)現(xiàn):文件(FileSystemResource
),ClassPath資源(ClassPathResource
)呀非,URL資源(UrlResource
),InputStream資源(InputStreamResource
),Byte數(shù)組(ByteArrayResource
)等。
具體實(shí)現(xiàn)也很簡單岸裙,ClassPathResource是通過ClassLoader進(jìn)行讀取文件的:
得到了配置流后猖败,就交給了XmlBeanDefinitionReader
進(jìn)行解析。
在初始化父類的時(shí)候降允,忽略裝配一些類
加載Bean
- 封裝資源文件恩闻。當(dāng)進(jìn)入
XmlBeanDefinitionReader
后首先對參數(shù)Resource使用EncodeResource
類進(jìn)行封裝。 - 獲取輸入流拟糕。從Resource中獲取對應(yīng)的
InputSttream
并構(gòu)造InputSource
判呕。 - 通過構(gòu)造的
InputSource
實(shí)例和Resource實(shí)例繼續(xù)調(diào)用方法doLoadBeanDefinitions
。
EncodeedResource
主要用于對資源文件的編碼進(jìn)行相應(yīng)的編碼處理送滞。
如果設(shè)置了編碼屬性侠草,會(huì)使用相應(yīng)的編碼作為輸入流的編碼。
這個(gè)方法主要是將重新編碼的輸入流轉(zhuǎn)換為SAX的InputSource對象犁嗅,同時(shí)如果指定了編碼边涕,需要設(shè)置相關(guān)的屬性。
這個(gè)方法處理了兩件事情:
- 加載XML文件褂微,得到對應(yīng)的Document.
- 根據(jù)返回的Document注冊bean
XML的驗(yàn)證模式
XML文件的驗(yàn)證模式保證了XML文件的正確性功蜓,而比較常用的驗(yàn)證模式有兩種:DTD和XSD。
DTD
DTD(Document Type Dedfinition
)即文檔類型定義宠蚂,是一種XML約束模式語言式撼,是XML文件的驗(yàn)證機(jī)制,屬于XML文件組成的一部分求厕。DTD是一種保證XML文檔格式正確的有效方法著隆,可以通過比較XML文檔和DTD文件來看文檔是否符合規(guī)范,元素和標(biāo)簽使用是否正確呀癣。一個(gè)DTD文檔包含:元素的定義規(guī)則美浦,元素間關(guān)系的定義規(guī)則,元素可以使用的屬性项栏,可以使用的實(shí)體或符號規(guī)則浦辨。
要使用DTD驗(yàn)證模式的時(shí)候需要在XML文件的頭部聲明:
DTD文件里面是一些ENTITY節(jié)點(diǎn)
XSD
XML Schema語言就是XSD(XML Schema Definition
)。Xml Schema
描述了XML文檔的結(jié)構(gòu)沼沈×鞒辏可以用一個(gè)指定的XML Schema
來驗(yàn)證某個(gè)XML文檔,已檢查改XML文檔是否符合其要求列另。文檔設(shè)計(jì)者可以通過過XML Schema
指定一個(gè)XML文檔所允許的結(jié)構(gòu)和內(nèi)容康吵,并可以據(jù)此檢查一個(gè)XML文檔是否是以有效的。XML Schema
本身是一個(gè)XML文檔访递,他符合XML語法結(jié)構(gòu),可以使用通用的XML解析器解析它同辣。
在使用XML Schema
文檔對XML實(shí)例文檔進(jìn)行檢驗(yàn)拷姿,除了要聲明空間外(xmlns=....
)惭载,還必須指定該名稱空間鎖對應(yīng)的XML Schema
文檔的存儲(chǔ)位置。通過schemaLocation
屬性來指定名稱空間所對一樣的XML Schema
文檔的存儲(chǔ)位置响巢。它包含兩個(gè)部分描滔,一部分是名稱空間的URL,另一部分就是該名稱空間所表示的XML Schema
文件位置或者URL地址踪古。
獲取Document
調(diào)用了DocumentLoader
接口的方法:
這個(gè)接口只有一個(gè)實(shí)現(xiàn)類DefaultDocumentLoader
BeanDefinitions
將配置文件轉(zhuǎn)換為Document
后含长,就會(huì)根據(jù)Document
對象進(jìn)行注冊Bean.
注冊bean:
首先通過反射獲取BeanDefinitionDocumentReader
的對象。
然后記錄下本次注冊前伏穆,已經(jīng)有多少個(gè)bean被注冊了拘泞。
然后調(diào)用documentReader
對象進(jìn)行注冊。
使用的還是默認(rèn)實(shí)現(xiàn)
在使用documentReader進(jìn)行讀取時(shí)枕扫,首先讀取的是root節(jié)點(diǎn)陪腌。
接下來就是解析的核心邏輯了:
首先處理profile
解析bean就是這里了