?
Dokumen ini menggunakan Manual laman web PHP Cina Lepaskan
在Spring中,那些組成你應(yīng)用程序的主體(backbone)及由Spring IoC容器所管理的對(duì)象,被稱之為bean。 簡單地講,bean就是由Spring容器初始化、裝配及管理的對(duì)象,除此之外,bean就與應(yīng)用程序中的其他對(duì)象沒有什么區(qū)別了。 而bean定義以及bean相互間的依賴關(guān)系將通過配置元數(shù)據(jù)來描述。
org.springframework.beans.factory.BeanFactory
是Spring IoC容器的實(shí)際代表者,IoC容器負(fù)責(zé)容納此前所描述的bean,并對(duì)bean進(jìn)行管理。
在Spring中,BeanFactory
是IoC容器的核心接口。
它的職責(zé)包括:實(shí)例化、定位、配置應(yīng)用程序中的對(duì)象及建立這些對(duì)象間的依賴。
Spring為我們提供了許多易用的BeanFactory
實(shí)現(xiàn),
XmlBeanFactory
就是最常用的一個(gè)。該實(shí)現(xiàn)將以XML方式描述組成應(yīng)用的對(duì)象
以及對(duì)象間的依賴關(guān)系。XmlBeanFactory
類將獲取此XML配
置元數(shù)據(jù),并用它來構(gòu)建一個(gè)完全可配置的系統(tǒng)或應(yīng)用。
Spring IoC 容器
從上圖可以看到,Spring IoC容器將讀取配置元數(shù)據(jù); 并通過它對(duì)應(yīng)用中各個(gè)對(duì)象進(jìn)行實(shí)例化、配置以及組裝。通常情況下我們使用簡單直觀 的XML來作為配置元數(shù)據(jù)的描述格式。在XML配置元數(shù)據(jù)中我們可以對(duì)那些我們希望通過 Spring IoC容器管理的bean進(jìn)行定義。
到目前為止,基于XML的元數(shù)據(jù)是最常用到的配置元數(shù)據(jù)格式。然而,它并 不是唯一的描述格式。Spring IoC容器在這一點(diǎn)上是 完全開放的。由于采用基于XML的配置元數(shù)據(jù)格式非常簡單, 因此 本章節(jié)的大部分內(nèi)容將采用該格式來說明Spring IoC容器的關(guān)鍵概念和功能
同時(shí)你也可以在第?3.11?節(jié) “基于注解(Annotation-based)的配置”這一節(jié)中 看到Spring容器支持的另一種元數(shù)據(jù)格式的詳細(xì)內(nèi)容。
在大多數(shù)的應(yīng)用程序中,并不需要用顯式的代碼去實(shí)例化一個(gè)或多個(gè)的Spring IoC
容器實(shí)例。例如,在web應(yīng)用程序中,我們只需要在web.xml
中添加
(大約)8 行簡單的XML描述符即可(參見第?3.8.5?節(jié) “ApplicationContext
在WEB應(yīng)用中的實(shí)例化”)。
Spring IoC容器至少包含一個(gè)bean定義,但大多數(shù)情況下會(huì)有多個(gè)bean定義。當(dāng)使用
基于XML的配置元數(shù)據(jù)時(shí),將在頂層的<beans/>
元素中配置一個(gè)
或多個(gè)<bean/>
元素。
bean定義與應(yīng)用程序中實(shí)際使用的對(duì)象一一對(duì)應(yīng)。通常情況下bean的定義包括:服務(wù)
層對(duì)象、數(shù)據(jù)訪問層對(duì)象(DAO)、類似Struts Action
的
表示層對(duì)象、Hibernate SessionFactory
對(duì)象、JMS
Queue
對(duì)象等等。通常bean的定義并不與容器中的領(lǐng)域
對(duì)象相同,因?yàn)轭I(lǐng)域?qū)ο蟮膭?chuàng)建和加載必須依賴具體的DAO和業(yè)務(wù)邏輯。.
以下是一個(gè)基于XML的配置元數(shù)據(jù)的基本結(jié)構(gòu):
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="..." class="..."> <!-- collaborators and configuration for this bean go here --> </bean> <bean id="..." class="..."> <!-- collaborators and configuration for this bean go here --> </bean> <!-- more bean definitions go here --> </beans>
Spring IoC容器的實(shí)例化非常簡單,如下面的例子:
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[] {"services.xml", "daos.xml"});
// an ApplicationContext
is also a BeanFactory
(via inheritance)
BeanFactory factory = context;
將XML配置文件分拆成多個(gè)部分是非常有用的。為了加載多個(gè)XML文件生成一個(gè) ApplicationContext實(shí)例,可以將文件路徑作為字符串?dāng)?shù)組傳給ApplicationContext構(gòu)造器 。而bean factory將通過調(diào)用bean defintion reader從多個(gè)文件中讀取bean定義。
通常情況下,Spring團(tuán)隊(duì)傾向于上述做法,因?yàn)檫@樣各個(gè)配置并不會(huì)查覺到它們
與其他配置文件的組合。另外一種方法是使用一個(gè)或多個(gè)的<import/>
元素
來從另外一個(gè)或多個(gè)文件加載bean定義。所有的<import/>
元素必
須在<bean/>
元素之前完成bean定義的導(dǎo)入。 讓我們看個(gè)例子:
<beans> <import resource="services.xml"/> <import resource="resources/messageSource.xml"/> <import resource="/resources/themeSource.xml"/> <bean id="bean1" class="..."/> <bean id="bean2" class="..."/> </beans>
在上面的例子中,我們從3個(gè)外部文件:services.xml
、
messageSource.xml
及themeSource.xml
來加載bean定義。這里采用的都是相對(duì)路徑,因此,此例中的services.xml
一定要與導(dǎo)入文件放在同一目錄或類路徑,而messageSource.xm
l
和themeSource.xml
的文件位置必須放在導(dǎo)入文件所
在目錄下的resources
目錄中。正如你所看到的那樣,開頭的斜杠
‘/’實(shí)際上可忽略。因此不用斜杠‘/’可能會(huì)更好一點(diǎn)。根據(jù)Spring XML配置文件的
Schema(或DTD),被導(dǎo)入文件必須是完全有效的XML bean定義文件,且根節(jié)點(diǎn)必須為
<beans/>
元素。
Spring IoC容器將管理一個(gè)或多個(gè)bean,這些bean
將通過配置文件中的bean定義被創(chuàng)建(在XML格式中為<bean/>
元素)。
在容器內(nèi)部,這些bean定義由BeanDefinition
對(duì)象來表示,該定義將包含以下信息:
全限定類名:這通常就是已定義bean的實(shí)際實(shí)現(xiàn)類。
bean行為的定義,這些定義將決定bean在容器中的行為(作用域、生命周期回調(diào)等等)
對(duì)其他bean的引用,這些引用bean也可以稱之為協(xié)作bean(collaborators) 或依賴bean(dependencies).
創(chuàng)建bean實(shí)例時(shí)的其他配置設(shè)置。比如使用bean來定義連接池,可以通過屬性或者構(gòu) 造參數(shù)指定連接數(shù),以及連接池大小限制等。
上述內(nèi)容直接被翻譯為每個(gè)bean定義包含的一組properties。下面的表格列出了部分 內(nèi)容的詳細(xì)鏈接:
表?3.1.?bean定義
名稱 | 鏈接 |
---|---|
class |
第?3.2.3.2?節(jié) “實(shí)例化bean” |
name |
第?3.2.3.1?節(jié) “bean的命名” |
scope |
第?3.4?節(jié) “Bean的作用域” |
constructor arguments |
第?3.3.1?節(jié) “注入依賴” |
properties |
第?3.3.1?節(jié) “注入依賴” |
autowiring mode |
第?3.3.5?節(jié) “自動(dòng)裝配(autowire)協(xié)作者” |
dependency checking mode |
第?3.3.6?節(jié) “依賴檢查” |
lazy-initialization mode |
第?3.3.4?節(jié) “延遲初始化bean” |
initialization method |
第?3.5.1.1?節(jié) “初始化回調(diào)” |
destruction method |
第?3.5.1.2?節(jié) “析構(gòu)回調(diào)” |
除了通過bean定義來描述要?jiǎng)?chuàng)建的指定bean的屬性之外,某些
BeanFactory
的實(shí)現(xiàn)也允許將那些非BeanFactory創(chuàng)建的、已有的用戶
對(duì)象注冊(cè)到容器中,比如使用DefaultListableBeanFactory
的registerSingleton(..)
方法。不過大多數(shù)應(yīng)用還是采用
元數(shù)據(jù)定義為主。
每個(gè)bean都有一個(gè)或多個(gè)id
(或稱之為標(biāo)識(shí)符或名稱,在術(shù)語
上可以理解成一回事)。這些id
在當(dāng)前IoC容器中必須唯一。如果
一個(gè)bean有多個(gè)id,那么其他的id在本質(zhì)上將被認(rèn)為是別名。
當(dāng)使用基于XML的配置元數(shù)據(jù)時(shí),將通過id
或
name
屬性來指定bean標(biāo)識(shí)符。id
屬性具有唯一性,
而且是一個(gè)真正的XML ID屬性,因此其他xml元素在引用該id時(shí),可以利用XML解析器的
驗(yàn)證功能。通常情況下最好為bean指定一個(gè)id。盡管XML規(guī)范規(guī)定了XML ID命名的有效
字符,但是bean標(biāo)識(shí)符的定義不受該限制,因?yàn)槌耸褂弥付ǖ腦ML字符來作為id,還可
以為bean指定別名,要實(shí)現(xiàn)這一點(diǎn)可以在name
屬性中使用逗號(hào)、
冒號(hào)或者空格將多個(gè)id分隔。
值得注意的是,為一個(gè)bean提供一個(gè)name并不是必須的,如果沒有指定,那么容 器將為其生成一個(gè)惟一的name。對(duì)于不指定name屬性的原因我們會(huì)在后面介紹(比如 內(nèi)部bean就不需要)。
在對(duì)bean進(jìn)行定義時(shí),除了使用id
屬性來指定名稱
之外,為了提供多個(gè)名稱,需要通過name
屬性來加以指定
。而所有的這些名稱都指向同一個(gè)bean,在某些情況下提供別名非常有用,比如
為了讓應(yīng)用的每一個(gè)組件能更容易的對(duì)公共組件進(jìn)行引用。
然而,在定義bean時(shí)就指定所有的別名并不是總是恰當(dāng)?shù)?。有時(shí)我們期望
能在當(dāng)前位置為那些在別處定義的bean引入別名。在XML配置文件中,可用
<alias/>
元素來完成bean別名的定義。如:
<alias name="fromName" alias="toName"/>
這里如果在容器中存在名為fromName
的bean定義,
在增加別名定義之后,也可以用toName
來引用。
考慮一個(gè)更為具體的例子,組件A在XML配置文件中定義了一個(gè)名為 componentA-dataSource的DataSource bean。但組件B卻想在其XML文件中 以componentB-dataSource的名字來引用此bean。而且在主程序MyApp的XML配 置文件中,希望以myApp-dataSource的名字來引用此bean。最后容器加載三個(gè) XML文件來生成最終的ApplicationContext,在此情形下,可通過在MyApp XML 文件中添加下列alias元素來實(shí)現(xiàn):
<alias name="componentA-dataSource" alias="componentB-dataSource"/> <alias name="componentA-dataSource" alias="myApp-dataSource" />
這樣一來,每個(gè)組件及主程序就可通過唯一名字來引用同一個(gè)數(shù)據(jù)源而互不干擾。
從本質(zhì)上來說,bean定義描述了如何創(chuàng)建一個(gè)或多個(gè)對(duì)象實(shí)例。當(dāng)需要的時(shí)候, 容器會(huì)從bean定義列表中取得一個(gè)指定的bean定義,并根據(jù)bean定義里面的配置元數(shù)據(jù) 使用反射機(jī)制來創(chuàng)建(或取得)一個(gè)實(shí)際的對(duì)象。
當(dāng)采用XML描述配置元數(shù)據(jù)時(shí),將通過<bean/>
元素的
class
屬性來指定實(shí)例化對(duì)象的類型。class
屬性 (對(duì)應(yīng)BeanDefinition
實(shí)例的
Class
屬性)通常是必須的(不過也有兩種例外的情形,見
第?3.2.3.2.3?節(jié) “使用實(shí)例工廠方法實(shí)例化”和
第?3.6?節(jié) “bean定義的繼承”)。class屬性主要有兩種用途
:在大多數(shù)情況下,容器將直接通過反射調(diào)用指定類的構(gòu)造器來創(chuàng)建bean(這有點(diǎn)類似于
在Java代碼中使用new操作符);在極少數(shù)情況下,容器將調(diào)用
類的靜態(tài)
工廠方法來創(chuàng)建bean實(shí)例,class
屬性將用來指定實(shí)際具有靜態(tài)
工廠方法的類(至于調(diào)用靜態(tài)工廠
方法創(chuàng)建的對(duì)象類型是當(dāng)前class還是其他的class則無關(guān)緊要)。
當(dāng)采用構(gòu)造器來創(chuàng)建bean實(shí)例時(shí),Spring對(duì)class并沒有特殊的要求, 我們通常使用的class都適用。也就是說,被創(chuàng)建的類并不需要實(shí)現(xiàn)任何特定的 接口,或以特定的方式編碼,只要指定bean的class屬性即可。不過根據(jù)所采用 的IoC類型,class可能需要一個(gè)默認(rèn)的空構(gòu)造器。
此外,IoC容器不僅限于管理JavaBean,它可以管理任意 的類。不過大多數(shù)使用Spring的人喜歡使用實(shí)際的JavaBean(具有默認(rèn)的(無參)構(gòu)造器 及setter和getter方法),但在容器中使用非bean形式(non-bean style)的類也是可 以的。比如遺留系統(tǒng)中的連接池,很顯然它與JavaBean規(guī)范不符,但Spring也能管理它。
當(dāng)使用基于XML的元數(shù)據(jù)配置文件,可以這樣來指定bean類:
<bean id="exampleBean" class="examples.ExampleBean"/> <bean name="anotherExample" class="examples.ExampleBeanTwo"/>
給構(gòu)造函數(shù)指定參數(shù)以及為bean實(shí)例設(shè)置屬性將在隨后的 部分中談及。
當(dāng)采用靜態(tài)工廠方法創(chuàng)建bean時(shí),除了需要指定class
屬性外,還需要通過factory-method
屬性來指定創(chuàng)建bean實(shí)例
的工廠方法。Spring將調(diào)用此方法(其可選參數(shù)接下來介紹)返回實(shí)例對(duì)象,就此而言,
跟通過普通構(gòu)造器創(chuàng)建類實(shí)例沒什么兩樣。
下面的bean定義展示了如何通過工廠方法來創(chuàng)建bean實(shí)例。注意,此定義并
未指定返回對(duì)象的類型,僅指定該類包含的工廠方法。在此例中,
createInstance()
必須是一個(gè)static方法。
<bean id="exampleBean" class="examples.ExampleBean2" factory-method="createInstance"/>
給工廠方法指定參數(shù)以及為bean實(shí)例設(shè)置屬性將在隨后的部份中談及。
與
使用靜態(tài)工廠方法實(shí)例化類似,用來進(jìn)行實(shí)例化的非靜態(tài)實(shí)例工廠方法位
于另外一個(gè)bean中,容器將調(diào)用該bean的工廠方法來創(chuàng)建一個(gè)新的bean實(shí)例。為使
用此機(jī)制,class
屬性必須為空,而factory-bean
屬性必須指定為當(dāng)前(或其祖先)容器中包含工廠方法的bean的名稱,而該
工廠bean的工廠方法本身必須通過factory-method
屬性來設(shè)定。
<!-- the factory bean, which contains a method called createInstance()
-->
<bean id="serviceLocator" class="com.foo.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- the bean to be created via the factory bean -->
<bean id="exampleBean"
factory-bean="serviceLocator"
factory-method="createInstance"/>
雖然設(shè)置bean屬性 的機(jī)制仍然在這里被提及,但隱式的做法是由工廠bean自己來管理以及通過依 賴注入(DI)來進(jìn)行配置。
Spring文檔中的factory bean指的是配置在Spring容器中通過使用
實(shí)例
或
靜態(tài)工廠方法創(chuàng)建對(duì)象的一種bean。而文檔中的FactoryBean
(注意首字母大寫)指的是Spring特有的
FactoryBean
。
從本質(zhì)上講,BeanFactory
僅僅只是一個(gè)
維護(hù)bean定義以及相互依賴關(guān)系的高級(jí)工廠接口。通過BeanFactory
我們可以訪問bean定義。下面的例子創(chuàng)建了一個(gè)bean工廠,此工廠
將從xml文件中讀取bean定義:
Resource res = new FileSystemResource("beans.xml"); BeanFactory factory = new XmlBeanFactory(res);
基本上就這些了,接著使用getBean(String)
方法就可以取得bean的實(shí)例;BeanFactory
提供的方法極其簡單。 BeanFactory
接口提供
了非常多的方法,但是對(duì)于我們的應(yīng)用來說,最好永遠(yuǎn)不要調(diào)用它們,當(dāng)然也包括
使用getBean(String)
方法,這樣可以避免我們對(duì)
Spring API的依賴。