日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Spring - Java/J2EE Application Framework 应用框架 第 3 章 Beans, BeanFactory和ApplicationContext

發(fā)布時(shí)間:2025/3/21 java 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring - Java/J2EE Application Framework 应用框架 第 3 章 Beans, BeanFactory和ApplicationContext 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

第?3?章?Beans, BeanFactory和ApplicationContext

3.1.?簡(jiǎn)介

在Spring中,兩個(gè)最基本最重要的包是?org.springframework.beans?和?org.springframework.context. 這兩個(gè)包中的代碼為Spring的反向控制?特性(也叫作依賴注射)提供了基礎(chǔ)。?BeanFactory提供了一種先進(jìn)的配置機(jī)制來管理任何種類bean(對(duì)象),這種配置機(jī)制考慮到任何一種可能的存儲(chǔ)方式。?ApplicationContext建立在BeanFactory之上,并增加了其他的功能,比如更容易同Spring AOP特性整合, 消息資源處理(用于國際化),事件傳遞,以聲明的方式創(chuàng)建ApplicationContext, 可選的父上下文和與應(yīng)用層相關(guān)的上下文(比如WebApplicationContext),以及其他方面的增強(qiáng)。

簡(jiǎn)而言之,BeanFactory提供了配置框架和基本的功能, 而?ApplicationContext為它增加了更強(qiáng)的功能,這些功能中的一些或許更加接近J2EE并且圍繞企業(yè)級(jí)應(yīng)用。一般來說,ApplicationContext是BeanFactory的完全超集, 任何BeanFactory功能和行為的描述也同樣被認(rèn)為適用于ApplicationContext

用戶有時(shí)不能確定BeanFactory和ApplicationContext中哪一個(gè)在特定場(chǎng)合下更適合。 通常大部分在J2EE環(huán)境的應(yīng)用中,最好選擇使用ApplicationContext, 因?yàn)樗粌H提供了BeanFactory所有的特性以及它自己附加的特性,而且還提供以聲明的方式使用一些功能, 這通常是令人滿意的。BeanFactory主要是在非常關(guān)注內(nèi)存使用的情況下 (比如在一個(gè)每kb都要計(jì)算的applet中)使用,而且你也不需要用到ApplicationContext的所有特性。

這一章粗略地分為兩部分,第一部分包括對(duì)BeanFactory和ApplicationContext都適用的一些基本原則。第二部分包括僅僅適用于ApplicationContext的一些特性

3.2.?BeanFactory 和 BeanDefinitions - 基礎(chǔ)

3.2.1.?BeanFactory

BeanFactory實(shí)際上是實(shí)例化,配置和管理眾多bean的容器。 這些bean通常會(huì)彼此合作,因而它們之間會(huì)產(chǎn)生依賴。 BeanFactory使用的配置數(shù)據(jù)可以反映這些依賴關(guān)系中 (一些依賴可能不像配置數(shù)據(jù)一樣可見,而是在運(yùn)行期作為bean之間程序交互的函數(shù))。

一個(gè)BeanFactory可以用接口org.springframework.beans.factory.BeanFactory表示, 這個(gè)接口有多個(gè)實(shí)現(xiàn)。 最常使用的的簡(jiǎn)單的eanFactory實(shí)現(xiàn)是org.springframework.beans.factory.xml.XmlBeanFactory。 (這里提醒一下:ApplicationContext是BeanFactory的子類, 所以大多數(shù)的用戶更喜歡使用ApplicationContext的XML形式)。

雖然大多數(shù)情況下,幾乎所有被BeanFactory管理的用戶代碼都不需要知道BeanFactory, 但是BeanFactory還是以某種方式實(shí)例化??梢允褂孟旅娴拇a實(shí)例化BeanFactory:

InputStream is = new FileInputStream("beans.xml"); XmlBeanFactory factory = new XmlBeanFactory(is);

或者

ClassPathResource res = new ClassPathResource("beans.xml"); XmlBeanFactory factory = new XmlBeanFactory(res);

或者

ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] {"applicationContext.xml", "applicationContext-part2.xml"}); // of course, an ApplicationContext is just a BeanFactory BeanFactory factory = (BeanFactory) appContext;

很多情況下,用戶代碼不需要實(shí)例化BeanFactory, 因?yàn)镾pring框架代碼會(huì)做這件事。例如,web層提供支持代碼,在J2EE web應(yīng)用啟動(dòng)過程中自動(dòng)載入一個(gè)Spring ApplicationContext。這個(gè)聲明過程在這里描述:

編程操作BeanFactory將會(huì)在后面提到,下面部分將集中描述BeanFactory的配置.

一個(gè)最基本的BeanFactory配置由一個(gè)或多個(gè)它所管理的Bean定義組成。在一個(gè)XmlBeanFactory中,根節(jié)點(diǎn)beans中包含一個(gè)或多個(gè)bean元素。

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"><beans><bean id="..." class="...">...</bean><bean id="..." class="...">...</bean>...</beans>

3.2.2.?BeanDefinition

一個(gè)XmlBeanFactory中的Bean定義包括的內(nèi)容有:

  • classname:這通常是bean的真正的實(shí)現(xiàn)類。但是如果一個(gè)bean使用一個(gè)靜態(tài)工廠方法所創(chuàng)建而不是被普通的構(gòu)造函數(shù)創(chuàng)建,那么這實(shí)際上就是工廠類的classname

  • bean行為配置元素:它聲明這個(gè)bean在容器的行為方式(比如prototype或singleton,自動(dòng)裝配模式,依賴檢查模式,初始化和析構(gòu)方法)

  • 構(gòu)造函數(shù)的參數(shù)和新創(chuàng)建bean需要的屬性:舉一個(gè)例子,一個(gè)管理連接池的bean使用的連接數(shù)目(即可以指定為一個(gè)屬性,也可以作為一個(gè)構(gòu)造函數(shù)參數(shù)),或者池的大小限制

  • 和這個(gè)bean工作相關(guān)的其他bean:比如它的合作者(同樣可以作為屬性或者構(gòu)造函數(shù)的參數(shù))。這個(gè)也被叫做依賴。

上面列出的概念直接轉(zhuǎn)化為組成bean定義的一組元素。這些元素在下面的表格中列出,它們每一個(gè)都有更詳細(xì)的說明的鏈接。

表?3.1.?Bean定義的解釋

特性詳細(xì)說明
class第?3.2.3?節(jié) “bean的類”
id和name第?3.2.4?節(jié) “Bean的標(biāo)志符 (id與name)”
singleton或prototype第?3.2.5?節(jié) “Singleton的使用與否”
構(gòu)造函數(shù)參數(shù)第?3.3.1?節(jié) “設(shè)置bean的屬性和合作者”
bean的屬性第?3.3.1?節(jié) “設(shè)置bean的屬性和合作者”
自動(dòng)裝配模式第?3.3.5?節(jié) “自動(dòng)裝配協(xié)作對(duì)象”
依賴檢查模式第?3.3.6?節(jié) “依賴檢查”
初始化模式第?3.4.1?節(jié) “生命周期接口”
析構(gòu)方法第?3.4.1?節(jié) “生命周期接口”

注意bean定義可以表示為真正的接口org.springframework.beans.factory.config.BeanDefinition以及它的各種子接口和實(shí)現(xiàn)。然而,絕大多數(shù)的用戶代碼不需要與BeanDefination直接接觸。

3.2.3.?bean的類

class屬性通常是強(qiáng)制性的(參考第?3.2.3.3?節(jié) “通過實(shí)例工廠方法創(chuàng)建bean”和第?3.5?節(jié) “子bean定義”),有兩種用法。在絕大多數(shù)情況下,BeanFactory直接調(diào)用bean的構(gòu)造函數(shù)來"new"一個(gè)bean(相當(dāng)于調(diào)用new的Java代碼),class屬性指定了需要?jiǎng)?chuàng)建的bean的類。 在比較少的情況下,BeanFactory調(diào)用某個(gè)類的靜態(tài)的工廠方法來創(chuàng)建bean, class屬性指定了實(shí)際包含靜態(tài)工廠方法的那個(gè)類。 (至于靜態(tài)工廠方法返回的bean的類型是同一個(gè)類還是完全不同的另一個(gè)類,這并不重要)。

3.2.3.1.?通過構(gòu)造函數(shù)創(chuàng)建bean

當(dāng)使用構(gòu)造函數(shù)創(chuàng)建bean時(shí),所有普通的類都可以被Spring使用并且和Spring兼容。 這就是說,被創(chuàng)建的類不需要實(shí)現(xiàn)任何特定的接口或者按照特定的樣式進(jìn)行編寫。僅僅指定bean的類就足夠了。 然而,根據(jù)bean使用的IoC類型,你可能需要一個(gè)默認(rèn)的(空的)構(gòu)造函數(shù)。

另外,BeanFactory并不局限于管理真正的JavaBean,它也能管理任何你想讓它管理的類。 雖然很多使用Spring的人喜歡在BeanFactory中用真正的JavaBean (僅包含一個(gè)默認(rèn)的(無參數(shù)的)構(gòu)造函數(shù),在屬性后面定義相對(duì)應(yīng)的setter和getter方法),但是在你的BeanFactory中也可以使用特殊的非bean樣式的類。 舉例來說,如果你需要使用一個(gè)遺留下來的完全沒有遵守JavaBean規(guī)范的連接池, 不要擔(dān)心,Spring同樣能夠管理它。

使用XmlBeanFactory你可以像下面這樣定義你的bean class:

<bean id="exampleBean"class="examples.ExampleBean"/> <bean name="anotherExample"class="examples.ExampleBeanTwo"/>

至于為構(gòu)造函數(shù)提供(可選的)參數(shù),以及對(duì)象實(shí)例創(chuàng)建后設(shè)置實(shí)例屬性,將會(huì)在后面敘述

3.2.3.2.?通過靜態(tài)工廠方法創(chuàng)建Bean

當(dāng)你定義一個(gè)使用靜態(tài)工廠方法創(chuàng)建的bean,同時(shí)使用class屬性指定包含靜態(tài)工廠方法的類,這個(gè)時(shí)候需要factory-method屬性來指定工廠方法名。Spring調(diào)用這個(gè)方法(包含一組可選的參數(shù))并返回一個(gè)有效的對(duì)象,之后這個(gè)對(duì)象就完全和構(gòu)造方法創(chuàng)建的對(duì)象一樣。用戶可以使用這樣的bean定義在遺留代碼中調(diào)用靜態(tài)工廠。

下面是一個(gè)bean定義的例子,聲明這個(gè)bean要通過factory-method指定的方法創(chuàng)建。注意這個(gè)bean定義并沒有指定返回對(duì)象的類型,只指定包含工廠方法的類。在這個(gè)例子中,createInstance?必須是static方法 .

<bean id="exampleBean"class="examples.ExampleBean2"factory-method="createInstance"/>

至于為工廠方法提供(可選的)參數(shù),以及對(duì)象實(shí)例被工廠方法創(chuàng)建后設(shè)置實(shí)例屬性,將會(huì)在后面敘述.

3.2.3.3.?通過實(shí)例工廠方法創(chuàng)建bean

使用一個(gè)實(shí)例工廠方法(非靜態(tài)的)創(chuàng)建bean和使用靜態(tài)工廠方法非常類似,調(diào)用一個(gè)已存在的bean(這個(gè)bean應(yīng)該是工廠類型)的工廠方法來創(chuàng)建新的bean。

使用這種機(jī)制,class屬性必須為空,而且factory-bean屬性必須指定一個(gè)bean的名字,這個(gè)bean一定要在當(dāng)前的bean工廠或者父bean工廠中,并包含工廠方法。 而工廠方法本身仍然要通過factory-method屬性設(shè)置。

下面是一個(gè)例子:

<!-- The factory bean, which contains a method calledcreateInstance --> <bean id="myFactoryBean"class="...">... </bean> <!-- The bean to be created via the factory bean --> <bean id="exampleBean"factory-bean="myFactoryBean"factory-method="createInstance"/>

雖然我們要在后面討論設(shè)置bean的屬性,但是這個(gè)方法意味著工廠bean本身能夠被容器通過依賴注射來管理和配置

3.2.4.?Bean的標(biāo)志符 (id與name)

每一個(gè)bean都有一個(gè)或多個(gè)id(也叫作標(biāo)志符,或名字;這些名詞說的是一回事)。這些id在管理bean的BeanFactory或ApplicationContext中必須是唯一的。 一個(gè)bean差不多總是只有一個(gè)id,但是如果一個(gè)bean有超過一個(gè)的id,那么另外的那些本質(zhì)上可以認(rèn)為是別名。

在一個(gè)XmlBeanFactory中(包括ApplicationContext的形式), 你可以用id或者name屬性來指定bean的id(s),并且在這兩個(gè)或其中一個(gè)屬性中至少指定一個(gè)id。 id屬性允許你指定一個(gè)id,并且它在XML DTD(定義文檔)中作為一個(gè)真正的XML元素的ID屬性被標(biāo)記, 所以XML解析器能夠在其他元素指回向它的時(shí)候做一些額外的校驗(yàn)。正因如此,用id屬性指定bean的id是一個(gè)比較好的方式。 然而,XML規(guī)范嚴(yán)格限定了在XML ID中合法的字符。通常這并不是真正限制你, 但是如果你有必要使用這些字符(在ID中的非法字符),或者你想給bean增加其他的別名, 那么你可以通過name屬性指定一個(gè)或多個(gè)id(用逗號(hào),或者分號(hào);分隔)。

3.2.5.?Singleton的使用與否

Beans被定義為兩種部署模式中的一種:singleton或non-singleton。 (后一種也別叫作prototype,盡管這個(gè)名詞用的不精確因?yàn)樗⒉皇欠浅_m合)。 如果一個(gè)bean是singleton形態(tài)的,那么就只有一個(gè)共享的實(shí)例存在, 所有和這個(gè)bean定義的id符合的bean請(qǐng)求都會(huì)返回這個(gè)唯一的、特定的實(shí)例。

如果bean以non-singleton,prototype模式部署的話,對(duì)這個(gè)bean的每次請(qǐng)求都會(huì)創(chuàng)建一個(gè)新的bean實(shí)例。這對(duì)于例如每個(gè)user需要一個(gè)獨(dú)立的user對(duì)象這樣的情況是非常理想的。

Beans默認(rèn)被部署為singleton模式,除非你指定。要記住把部署模式變?yōu)閚on-singletion(prototype)后,每一次對(duì)這個(gè)bean的請(qǐng)求都會(huì)導(dǎo)致一個(gè)新創(chuàng)建的bean,而這可能并不是你真正想要的。所以僅僅在絕對(duì)需要的時(shí)候才把模式改成prototype。

在下面這個(gè)例子中,兩個(gè)bean一個(gè)被定義為singleton,而另一個(gè)被定義為non-singleton(prototype)??蛻舳嗣看蜗駼eanFactory請(qǐng)求都會(huì)創(chuàng)建新的exampleBean,而AnotherExample僅僅被創(chuàng)建一次;在每次對(duì)它請(qǐng)求都會(huì)返回這個(gè)實(shí)例的引用。

<bean id="exampleBean"class="examples.ExampleBean" singleton="false"/> <bean name="yetAnotherExample"class="examples.ExampleBeanTwo" singleton="true"/>

注意:當(dāng)部署一個(gè)bean為prototype模式,這個(gè)bean的生命周期就會(huì)有稍許改變。 通過定義,Spring無法管理一個(gè)non-singleton/prototype bean的整個(gè)生命周期, 因?yàn)楫?dāng)它創(chuàng)建之后,它被交給客戶端而且容器根本不再跟蹤它了。當(dāng)說起non-singleton/prototype bean的時(shí)候, 你可以把Spring的角色想象成“new”操作符的替代品。從那之后的任何生命周期方面的事情都由客戶端來處理 。BeanFactory中bean的生命周期將會(huì)在?第?3.4.1?節(jié) “生命周期接口”一節(jié)中有更詳細(xì)的敘述。

3.3.?屬性,合作者,自動(dòng)裝配和依賴檢查

3.3.1.?設(shè)置bean的屬性和合作者

反向控制通常與依賴注入同時(shí)提及?;镜囊?guī)則是bean通過以下方式來定義它們的依賴(比如它們與之合作的其他對(duì)象):構(gòu)造函數(shù)的參數(shù),工廠方法的參數(shù); 當(dāng)對(duì)象實(shí)例被構(gòu)造出來或從一個(gè)工廠方法返回后設(shè)置在這個(gè)實(shí)例上的屬性。容器的工作就是創(chuàng)建完bean之后,真正地注入這些依賴。這完全是和一般控制方式相反的(因此稱為反向控制),比如bean實(shí)例化,或者直接使用構(gòu)造函數(shù)定位依賴關(guān)系,或者類似Service Locator模式的東西。我們不會(huì)詳細(xì)闡述依賴注射的優(yōu)點(diǎn),很顯然通過使用它:代碼變得非常清晰;當(dāng)bean不再自己查找他們依賴的類而是由容器提供,甚至不需要知道這些類在哪里以及它們實(shí)際上是什么類型,這時(shí)高層次的解耦也變得很容易了。

正如上面提到的那樣,反向控制/依賴注射存在兩種主要的形式:

  • 基于setter的依賴注射,是在調(diào)用無參的構(gòu)造函數(shù)或無參的靜態(tài)工廠方法實(shí)例化你的bean之后, 通過調(diào)用你的bean上的setter方法實(shí)現(xiàn)的。 在BeanFactory中定義的使用基于setter方法的注射依賴的bean是真正的JavaBean。 Spring一般提倡使用基于setter方法的依賴注射,因?yàn)楹芏嗟臉?gòu)造函數(shù)參數(shù)將會(huì)是笨重的, 尤其在有些屬性是可選的情況下。

  • 基于構(gòu)造函數(shù)的依賴注射,它是通過調(diào)用帶有許多參數(shù)的構(gòu)造方法實(shí)現(xiàn)的, 每個(gè)參數(shù)表示一個(gè)合作者或者屬性。 另外,調(diào)用帶有特定參數(shù)的靜態(tài)工廠方法來構(gòu)造bean可以被認(rèn)為差不多等同的, 接下來的文字會(huì)把構(gòu)造函數(shù)的參數(shù)看成和靜態(tài)工廠方法的參數(shù)類似。 雖然Spring一般提倡在大多數(shù)情況下使用基于setter的依賴注射, 但是Spring還是完全支持基于構(gòu)造函數(shù)的依賴注射, 因?yàn)槟憧赡芟胍谀切┲惶峁┒鄥?shù)構(gòu)造函數(shù)并且沒有setter方法的遺留的bean上使用Spring。 另外對(duì)于一些比較簡(jiǎn)單的bean,一些人更喜歡使用構(gòu)造函數(shù)方法以確保bean不會(huì)處于錯(cuò)誤的狀態(tài)。

BeanFactory同時(shí)支持這兩種方式將依賴注射到被管理bean中。(實(shí)際上它還支持在一些依賴已經(jīng)通過構(gòu)造函數(shù)方法注射后再使用setter方法注射依賴)。依賴的配置是以BeanDefinition的形式出現(xiàn),它和JavaBeans的PropertyEditors一起使用從而知道如何把屬性從一個(gè)格式轉(zhuǎn)變?yōu)榱硪粋€(gè)。真正傳送的值被封裝為PropertyValue對(duì)象。然而,大多數(shù)Spring的使用者并不要直接(比如編程的方式)處理這些類,而更多地使用一個(gè)XML定義文件,這個(gè)文件會(huì)在內(nèi)部被轉(zhuǎn)變?yōu)檫@些類的實(shí)例,用來讀取整個(gè)BeanFactory或ApplicationContext。

Bean依賴的決定通常取決于下面這些內(nèi)容:

  • BeanFactory通過使用一個(gè)描述所有bean的配置被創(chuàng)建和實(shí)例化。大多數(shù)的Spring用戶使用一個(gè)支持XML格式配置文件的BeanFactory或ApplicationContext實(shí)現(xiàn)。

  • 每一個(gè)bean的依賴表現(xiàn)為屬性,構(gòu)造函數(shù)參數(shù),或者當(dāng)用靜態(tài)工廠方法代替普通構(gòu)造函數(shù)時(shí)工廠方法的參數(shù)。這些依賴將會(huì)在bean真正被創(chuàng)建出來后提供給bean。

  • 每一個(gè)屬性或者構(gòu)造函數(shù)參數(shù)要么是一個(gè)要被設(shè)置的值的定義,要么是一個(gè)指向BeanFactory中其他bean的引用。在ApplicationContext的情況下,這個(gè)引用可以指向一個(gè)父親ApplicationContext中bean。

  • 每一個(gè)屬性或構(gòu)造函數(shù)參數(shù)的值,必須能夠從(配置文件中)被指定的格式轉(zhuǎn)變?yōu)檎鎸?shí)類型。缺省情況下,Spring能夠把一個(gè)字符串格式的值轉(zhuǎn)變?yōu)樗袃?nèi)建的類型,比如int,?long,String,?boolean等等。另外當(dāng)說到基于XML的BeanFactory實(shí)現(xiàn)的時(shí)候(包括ApplicationContext實(shí)現(xiàn)),它們已經(jīng)為定義Lists, Maps, Sets和Properties集合類型提供了內(nèi)在的支持。另外,Spring通過使用JavaBeans的?PropertyEditor定義,能夠?qū)⒆址缔D(zhuǎn)變?yōu)槠渌我獾念愋汀?#xff08;你可以為PropertyEditor提供你自己的PropertyEditor定義從而能夠轉(zhuǎn)變你自定義的類型;更多關(guān)于PropertyEditors的信息以及如何手工增加自定義的PropertyEditors請(qǐng)參看第?3.9?節(jié) “注冊(cè)附加的定制PropertyEditor”)。當(dāng)一個(gè)bean屬性是一個(gè)Java Class類型,Spring允許你用這個(gè)類的名字的字符串作為這個(gè)屬性的值,ClassEditor?這個(gè)內(nèi)建的PropertyEditor會(huì)幫你把類的名字轉(zhuǎn)變成真實(shí)的Class實(shí)例。

  • 很重要的一點(diǎn)就是:Spring在BeanFactory創(chuàng)建的時(shí)候要校驗(yàn)BeanFactory中每一個(gè)Bean的配置。這些校驗(yàn)包括作為Bean引用的屬性必須實(shí)際引用一個(gè)合法的bean(比如被引用的bean也定義在BeanFactory中,或者當(dāng)ApplicationContext時(shí),在父親ApplicationContext中)。但是,bean屬性本身直到bean被真實(shí)建立的的時(shí)候才被設(shè)置。對(duì)于那些是singleton并且被設(shè)置為pre-instantiated的bean來說(比如一個(gè)ApplicationContext中的singletonbean),bean在創(chuàng)建BeanFactory的時(shí)候創(chuàng)建,但是對(duì)于其他情況,發(fā)生在bean被請(qǐng)求的時(shí)候。當(dāng)一個(gè)bean必須被創(chuàng)建時(shí),它會(huì)潛在地導(dǎo)致一系列的其他bean被創(chuàng)建,像它的依賴以及它的依賴的依賴(如此下去)被創(chuàng)建和賦值。

  • 通常你可以信任Spring做了正確的事情。它會(huì)在BeanFactory裝載的時(shí)候檢查出錯(cuò)誤,包括對(duì)不存在bean的引用和循環(huán)引用。它會(huì)盡可能晚地設(shè)置屬性和解決依賴(比如創(chuàng)建那些需要的依賴),也就是在bean真正被創(chuàng)建的時(shí)候。這就意味著:就算一個(gè)BeanFactory被正確地裝載,稍后當(dāng)你請(qǐng)求一個(gè)bean的時(shí)候,如果創(chuàng)建那個(gè)bean或者它的依賴的時(shí)候出現(xiàn)了錯(cuò)誤,這個(gè)BeanFactory也會(huì)拋出一個(gè)異常。比如,如果一個(gè)bean拋出一個(gè)異常作為缺少或非法屬性的結(jié)果,這樣的情況就會(huì)發(fā)生。這種潛在地推遲一些配置錯(cuò)誤可見性的行為正是ApplicationContext默認(rèn)預(yù)實(shí)例化singleton bean的原因。以前期的時(shí)間和內(nèi)存為代價(jià)在beans真正需要之前創(chuàng)建它們,你就可以在ApplicationContext創(chuàng)建的時(shí)候找出配置錯(cuò)誤,而不是在后來。如果你愿意,你也可以覆蓋這種默認(rèn)的行為,設(shè)置這些singleton bean為lazy-load(不是預(yù)實(shí)例化的)。

  • 幾個(gè)例子:

    首先,一個(gè)使用BeanFactory以及基于setter方法的依賴注射。下面是一個(gè)定義了一些bean的XmlBeanFactory?配置文件的一小部分。接下去是正式的bean的代碼,演示了正確的setter方法聲明。

    <bean id="exampleBean" class="examples.ExampleBean"><property name="beanOne"><ref bean="anotherExampleBean"/></property><property name="beanTwo"><ref bean="yetAnotherBean"/></property><property name="integerProperty"><value>1</value></property> </bean><bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

    public class ExampleBean {private AnotherBean beanOne;private YetAnotherBean beanTwo;private int i;public void setBeanOne(AnotherBean beanOne) {this.beanOne = beanOne;}public void setBeanTwo(YetAnotherBean beanTwo) {this.beanTwo = beanTwo;}public void setIntegerProperty(int i) {this.i = i;} }

    正如你所看到的一樣,setter方法被聲明以符合XML文件中指定的屬性。(XML文件中的屬性,直接對(duì)應(yīng)著RootBeanDefinition中的PropertyValues對(duì)象)

    接著是一個(gè)使用IoC type3(基于構(gòu)造函數(shù)的依賴注射)的BeanFactory。下面是XML配置中的一段,指定了構(gòu)造函數(shù)參數(shù)以及展示構(gòu)造函數(shù)的代碼:

    <bean id="exampleBean" class="examples.ExampleBean"><constructor-arg><ref bean="anotherExampleBean"/></constructor-arg><constructor-arg><ref bean="yetAnotherBean"/></constructor-arg><constructor-arg><value>1</value></constructor-arg> </bean><bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

    public class ExampleBean {private AnotherBean beanOne;private YetAnotherBean beanTwo;private int i;public ExampleBean(AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {this.beanOne = anotherBean;this.beanTwo = yetAnotherBean;this.i = i;} }

    正如你所看到的,bean定義中指定的構(gòu)造函數(shù)參數(shù)將會(huì)作為ExampleBean的構(gòu)造函數(shù)參數(shù)被傳入。

    現(xiàn)在考慮一下不用構(gòu)造函數(shù),而是調(diào)用一個(gè)靜態(tài)工廠方法來返回一個(gè)對(duì)象的實(shí)例:

    <bean id="exampleBean" class="examples.ExampleBean"factory-method="createInstance"><constructor-arg><ref bean="anotherExampleBean"/></constructor-arg><constructor-arg><ref bean="yetAnotherBean"/></constructor-arg><constructor-arg><value>1</value></constructor-arg> </bean><bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

    public class ExampleBean {...// a private constructorprivate ExampleBean(...) {...}// a static factory method// the arguments to this method can be considered the dependencies of the bean that// is returned, regardless of how those arguments are actually used.public static ExampleBean ExampleBean(AnotherBean anotherBean,YetAnotherBean yetAnotherBean, int i) {ExampleBean eb = new ExampleBean(...);// some other operations...return eb;} }

    需要注意的是:靜態(tài)工廠方法的參數(shù)由?constructor-arg元素提供,這和構(gòu)造函數(shù)的用法是一樣的。這些參數(shù)是可選的。重要的一點(diǎn)是工廠方法所返回的對(duì)象類型不一定和包含這個(gè)靜態(tài)工廠方法的類一致,雖然上面這個(gè)例子中是一樣的。前面所提到的實(shí)例工廠方法(non-static)用法基本上是一樣的(除了使用factory-bean屬性代替class屬性),在這里就不再詳細(xì)敘述了。 .

    3.3.2.?深入Bean屬性和構(gòu)造函數(shù)參數(shù)

    正如前面提到的那樣,bean的屬性和構(gòu)造函數(shù)參數(shù)可以被定義為其他bean的引用(合作者),或者內(nèi)聯(lián)定義的值。為了達(dá)到這個(gè)目的,XmlBeanFactory在property和constructor-arg元素中支持許多子元素類型。

    value元素用適合人讀的字符串形式指定屬性或構(gòu)造函數(shù)參數(shù)。正如前面提到的那樣,JavaBeans的PropertyEditors被用來將這些字符串從java.lang.String類型轉(zhuǎn)變?yōu)檎鎸?shí)的屬性類型或參數(shù)類型。

    <beans><bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"><!-- results in a setDriverClassName(String) call --><property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property><property name="url"><value>jdbc:mysql://localhost:3306/mydb</value></property><property name="username"><value>root</value></property></bean> </beans>

    null元素被用來處理null值。Spring將porperties等的空參數(shù)視為空的字符串。下面這個(gè)XmlBeanFactory配置:

    <bean class="ExampleBean"><property name="email"><value></value></property> </bean>

    導(dǎo)致email屬性被設(shè)置為””,同java代碼:exampleBean.setEmail("")等價(jià)。 而專門的<null>元素則可以用來指定一個(gè)null值,所以

    <bean class="ExampleBean"><property name="email"><null/></property> </bean>

    同代碼:exampleBean.setEmail(null)是等價(jià)的.

    list,?set,?map, 以及?props?元素可以用來定義和設(shè)置類型 為Java的List,Set,?Map, 和?Properties?.

    <beans>...<bean id="moreComplexObject" class="example.ComplexObject"><!-- results in a setPeople(java.util.Properties) call --><property name="people"><props><prop key="HarryPotter">The magic property</prop><prop key="JerrySeinfeld">The funny property</prop></props></property><!-- results in a setSomeList(java.util.List) call --><property name="someList"><list><value>a list element followed by a reference</value><ref bean="myDataSource"/></list></property><!-- results in a setSomeMap(java.util.Map) call --><property name="someMap"><map><entry key="yup an entry"><value>just some string</value></entry><entry key="yup a ref"><ref bean="myDataSource"/></entry></map></property><!-- results in a setSomeSet(java.util.Set) call --><property name="someSet"><set><value>just some string</value><ref bean="myDataSource"/></set></property></bean> </beans>

    注意:Map的entry或set的value,它們的值又可以是下面元素中的任何一個(gè):

    (bean | ref | idref | list | set | map | props | value | null)

    在property?元素中定義的bean元素用來定義一個(gè)內(nèi)聯(lián)的bean,而不是引用BeanFactory其他地方定義的bean。內(nèi)聯(lián)bean定義不需要任何id定義

    <bean id="outer" class="..."><!-- Instead of using a reference to target, just use an inner bean --><property name="target"><bean class="com.mycompany.PersonImpl"><property name="name"><value>Tony</value></property><property name="age"><value>51</value></property></bean></property> </bean>

    idref元素完全是一種簡(jiǎn)寫和防止錯(cuò)誤的方式,用來設(shè)置屬性值為容器中其他bean的id?或name。

    <bean id="theTargetBean" class="..."> </bean> <bean id="theClientBean" class="..."><property name="targetName"><idref bean="theTargetBean"/></property> </bean>

    這個(gè)在運(yùn)行時(shí)同下面的片段等價(jià):

    <bean id="theTargetBean" class="..."> </bean> <bean id="theClientBean" class="..."><property name="targetName"><value>theTargetBean</value></property> </bean>

    第一種形式比第二種形式更好的原因是:使用idref標(biāo)記將會(huì)使Spring在部署的時(shí)候就驗(yàn)證其他的bean是否真正存在; 在第二種形式中,targetName屬性的類僅僅在Spring實(shí)例化這個(gè)類的時(shí)候做它自己的驗(yàn)證,這很可能在容器真正部署完很久之后。

    另外,如果被引用的bean在同一個(gè)xml文件中而且bean的名稱是bean的?id,那么就可以使用local屬性。它會(huì)讓XML解析器更早,在XML文檔解析的時(shí)候,驗(yàn)證bean的名稱。

    <property name="targetName"><idref local="theTargetBean"/></property>

    ref元素是最后一個(gè)能在property元素中使用的元素。它是用來設(shè)置屬性值引用容器管理的其他bean(可以叫做合作者)。正如前一節(jié)提到的,擁有這些屬性的bean依賴被引用的bean,被引用的bean將會(huì)在屬性設(shè)置前,必要的時(shí)候需要時(shí)初始化(如果是一個(gè)singleton bean可能已經(jīng)被容器初始化)。所有的引用根本上是一個(gè)指向其他對(duì)象的引用,不過有3種形式指定被引用對(duì)象的id/name,這3種不同形式?jīng)Q定作用域和如何處理驗(yàn)證。

    用ref元素的bean屬性指定目標(biāo)bean是最常見的形式,它允許指向的bean可以在同一個(gè)BeanFactory/ApplicationContext(無論是否在同一個(gè)XML文件中)中,也可以在父BeanFactory/ApplicationContext中。bean屬性的值可以同目標(biāo)bean的id屬性相同,也可以同目標(biāo)bean的name屬性中任何一個(gè)值相同。

    <ref bean="someBean"/>

    用local屬性指定目標(biāo)bean可以利用XML解析器的能力在同一個(gè)文件中驗(yàn)證XML id引用。local屬性的值必須與目標(biāo)bean的id屬性一致。如果在同一個(gè)文件中沒有匹配的元素,XML解析器將會(huì)產(chǎn)生一個(gè)錯(cuò)誤。因此,如果目標(biāo)bean在同一個(gè)XML文件中,那么使用local形式將是最好的選擇(為了能夠盡可能早的發(fā)現(xiàn)錯(cuò)誤)。

    <ref local="someBean"/>

    用parent屬性指定目標(biāo)bean允許引用當(dāng)前BeanFactory(ApplicationContext)的父BeanFactory(ApplicationContext)中的bean。parent屬性的值可以同目標(biāo)bean的id屬性相同,也可以同目標(biāo)bean的name屬性中的一個(gè)值相同,而且目標(biāo)bean必須在當(dāng)前BeanFactory(ApplicationContext)的父BeanFactory(ApplicationContext)中。當(dāng)需要用某種proxy包裝一個(gè)父上下文中存在的bean(可能和父上下文中的有同樣的name),所以需要原始的對(duì)象用來包裝它。

    <ref parent="someBean"/>

    3.3.3.?方法注入

    對(duì)于大部分的用戶來說,容器中多數(shù)的bean是singleton的。當(dāng)一個(gè)singleton的bean需要同另一個(gè)singleton的 bean合作(使用)時(shí),或者一個(gè)非singleton的bean需要同另一個(gè)非singleton的bean合作的時(shí)候,通過定義一個(gè)bean為另一個(gè)bean的屬性來處理這種依賴的關(guān)系就足夠了。然而當(dāng)bean的生命周期不同的時(shí)候就有一個(gè)問題。想想一下一個(gè)singleton bean A, 或許在每次方法調(diào)用的時(shí)候都需要使用一個(gè)non-singleton bean B。容器僅僅會(huì)創(chuàng)建這個(gè)singleton bean A一次,因此僅僅有一次的機(jī)會(huì)去設(shè)置它的屬性。因此容器沒有機(jī)會(huì)每次去為bean A提供新的bean B的實(shí)例。

    一個(gè)解決這個(gè)問題的方法是放棄一些反向控制。Bean A可以通過實(shí)現(xiàn)?BeanFactoryAware知道容器的存在(參見這里)),使用編程的手段(參見這里)在需要的時(shí)候通過調(diào)用getBean("B")來向容器請(qǐng)求新的bean B實(shí)例。 因?yàn)閎ean的代碼知道Spring并且耦合于Spring,所以這通常不是一個(gè)好的方案。

    方法注入,BeanFactory的高級(jí)特性之一,可以以清潔的方式處理這種情況以及其他一些情況。

    3.3.3.1.?Lookup方法注入

    Lookup方法注射指容器能夠重寫容器中bean的抽象或具體方法,返回查找容器中其他bean的結(jié)果。 被查找的bean在上面描述的場(chǎng)景中通常是一個(gè)non-singleton bean (盡管也可以是一個(gè)singleton的)。Spring通過使用CGLIB庫在客戶端的類之上修改二進(jìn)制碼, 從而實(shí)現(xiàn)上述的場(chǎng)景要求。

    包含方法注入的客戶端類,必須按下面的形式的抽象(具體)定義方法:

    protected abstract SingleShotHelper createSingleShotHelper();

    如果方法不是抽象的,Spring就會(huì)直接重寫已有的實(shí)現(xiàn)。在XmlBeanFactory的情況下,你可以使用bean定義中的lookup-method?屬性來指示Spring去注入/重寫這個(gè)方法,以便從容器返回一個(gè)特定的bean。舉個(gè)例子說明:

    <!-- a stateful bean deployed as a protype (non-singleton) --> <bean id="singleShotHelper class="..." singleton="false"> </bean><!-- myBean uses singleShotHelper --> <bean id="myBean" class="..."><lookup-method name="createSingleShotHelper"bean="singleShotHelper"/><property>...</property> </bean>

    當(dāng)myBean需要一個(gè)新的singleShotHelper的實(shí)例的時(shí)候, 它就會(huì)調(diào)用它自己的createSingleShotHelper?方法。 值得注意的是:部署beans的人員必須小心地將singleShotHelper作為一個(gè)non-singleton部署 (如果確實(shí)需要這么做)。如果它作為一個(gè)singleton(除非明確說明,否則缺省就是singletion)而部署, 同一個(gè)singleShotHelper實(shí)例將會(huì)每次被返回。

    注意Lookup方法注射能夠同構(gòu)造函數(shù)注射結(jié)合(對(duì)創(chuàng)建的bean提供可選的構(gòu)造函數(shù)參數(shù)), 也可以同setter方法注射結(jié)合(在創(chuàng)建的bean之上設(shè)置屬性)。

    3.3.3.2.?任意方法的替換

    另一種方法注射沒有l(wèi)ookup方法注入用的多,它用另一個(gè)方法實(shí)現(xiàn)替換被管理bean的任意一個(gè)方法。用戶可以放心跳過這一節(jié)(這是個(gè)有點(diǎn)高級(jí)的特性),除非這個(gè)功能確實(shí)需要。

    在一個(gè)XmlBeanFactory中,對(duì)于一個(gè)被部署的bean,?replaced-method元素可以用來把已存在的方法實(shí)現(xiàn)替換為其他的實(shí)現(xiàn)。 考慮如下的類,有一個(gè)我們想要重寫的computeValue方法:

    ... public class MyValueCalculator {public String computeValue(String input) {... some real code}... some other methods }

    需要為新方法定義提供實(shí)現(xiàn)?org.springframework.beans.factory.support.MethodReplacer接口的類。

    /** meant to be used to override the existing computeValueimplementation in MyValueCalculator */ public class ReplacementComputeValue implements MethodReplacer {public Object reimplement(Object o, Method m, Object[] args) throws Throwable {// get the input value, work with it, and return a computed resultString input = (String) args[0];... return ...; }

    部署原始的類和指定方法重寫的BeanFactory部署定義象下面所示的 :

    <bean id="myValueCalculator class="x.y.z.MyValueCalculator"><!-- arbitrary method replacement --><replaced-method name="computeValue" replacer="replacementComputeValue"><arg-type>String</arg-type></replaced-method> </bean><bean id="replacementComputeValue" class="a.b.c.ReplaceMentComputeValue"> </bean>

    replaced-method元素中的一個(gè)或多個(gè)arg-type?元素用來表示, 這個(gè)被重載方法的方法簽名。 注意,參數(shù)的簽名只有在方法被重載并且該方法有多個(gè)不同的形式的時(shí)候才真正需要。 為了方便,參數(shù)的類型字符串可以使全限定名的子字符串。比如,以下的都匹配java.lang.String

    java.lang.StringStringStr

    因?yàn)閰?shù)的個(gè)數(shù)通常就足夠區(qū)別不同的可能,所以僅僅使用匹配參數(shù)的最短的字符串能夠節(jié)省很多鍵入工作。

    3.3.4.?使用?depends-on

    對(duì)于大多數(shù)的情況,一個(gè)bean被另一個(gè)bean依賴,是由這個(gè)bean是否被當(dāng)作其他bean的屬性來表達(dá)的。 在XmlBeanFactory中,它是通過ref元素來完成的。 與這種方式不同的是,有時(shí)一個(gè)知道容器的bean僅僅會(huì)被給與它所的依賴的id (使用一個(gè)字符串值或等價(jià)的idref元素)。接著第一個(gè)bean就以編程的方式地向容器請(qǐng)求它的依賴。 在兩種情況下,被依賴的bean都會(huì)在依賴它的bean之前被恰當(dāng)?shù)爻跏蓟?/p>

    對(duì)于相對(duì)罕見的情況,beans之間的依賴不夠直接(舉例,當(dāng)一個(gè)類中的靜態(tài)初始?jí)K需要被觸發(fā),比如數(shù)據(jù)庫驅(qū)動(dòng)的注冊(cè)) ,depends-on?元素可以用來在初始化使用這個(gè)元素的bean之前,強(qiáng)制一個(gè)或多個(gè)beans初始化。

    下面是一個(gè)配置的例子:

    <bean id="beanOne" class="ExampleBean" depends-on="manager"><property name="manager"><ref local="manager"/></property> </bean><bean id="manager" class="ManagerBean"/>

    3.3.5.?自動(dòng)裝配協(xié)作對(duì)象

    BeanFactory能夠自動(dòng)裝配合作bean之間的關(guān)系。這就意味著,讓Spring通過檢查BeanFactory的內(nèi)容來自動(dòng)裝配你的bean的合作者(也就是其他的bean)。自動(dòng)裝配功能有5種模式。自動(dòng)裝配可以指定給每一個(gè)bean,因此可以給一些bean使用而其他的bean不自動(dòng)裝配。通過使用自動(dòng)裝配,可以 減少(或消除)指定屬性(或構(gòu)造函數(shù)參數(shù))的需要,顯著節(jié)省鍵入工作。?[1]?在XmlBeanFactory中,使用bean元素的autowire屬性來指定bean定義的自動(dòng)裝配模式。以下是允許的值.

    表?3.2.?自動(dòng)裝配模式

    模式解釋
    no不使用自動(dòng)裝配。Bean的引用必須通過ref元素定義。這是默認(rèn)的配置,在較大的部署環(huán)境中不鼓勵(lì)改變這個(gè)配置,因?yàn)槊鞔_的指定合作者能夠得到更多的控制和清晰性。從某種程度上說,這也是系統(tǒng)結(jié)構(gòu)的文檔形式。
    byName通過屬性名字進(jìn)行自動(dòng)裝配。這個(gè)選項(xiàng)會(huì)會(huì)檢查BeanFactory,查找一個(gè)與將要裝配的屬性同樣名字的bean 。比如,你有一個(gè)bean的定義被設(shè)置為通過名字自動(dòng)裝配,它包含一個(gè)master屬性(也就是說,它有一個(gè)setMaster(...)方法),Spring就會(huì)查找一個(gè)叫做master的bean定義,然后用它來設(shè)置master屬性。
    byType如果BeanFactory中正好有一個(gè)同屬性類型一樣的bean,就自動(dòng)裝配這個(gè)屬性。如果有多于一個(gè)這樣的bean,就拋出一個(gè)致命異常,它指出你可能不能對(duì)那個(gè)bean使用byType的自動(dòng)裝配。如果沒有匹配的bean,則什么都不會(huì)發(fā)生,屬性不會(huì)被設(shè)置。如果這是你不想要的情況(什么都不發(fā)生),通過設(shè)置dependency-check="objects"屬性值來指定在這種情況下應(yīng)該拋出錯(cuò)誤。
    constructor這個(gè)同byType類似,不過是應(yīng)用于構(gòu)造函數(shù)的參數(shù)。如果在BeanFactory中不是恰好有一個(gè)bean與構(gòu)造函數(shù)參數(shù)相同類型,則一個(gè)致命的錯(cuò)誤會(huì)產(chǎn)生。
    autodetect通過對(duì)bean 檢查類的內(nèi)部來選擇constructorbyType。如果找到一個(gè)缺省的構(gòu)造函數(shù),那么就會(huì)應(yīng)用byType。

    注意:顯式的指定依賴,比如property和constructor-arg元素,總會(huì)覆蓋自動(dòng)裝配。自動(dòng)裝配的行為可以和依賴檢查結(jié)合使用,依賴檢查會(huì)在自動(dòng)裝配完成后發(fā)生。

    注意:正如我們已經(jīng)提到過的,對(duì)于大型的應(yīng)用,自動(dòng)裝配不鼓勵(lì)使用,因?yàn)樗コ四愕暮献黝惖耐该餍院徒Y(jié)構(gòu)。

    3.3.6.?依賴檢查

    對(duì)于部署在BeanFactory的bean的未解決的依賴,Spring有能力去檢查它們的存在性。 這些依賴要么是bean的JavaBean式的屬性,在bean的定義中并沒有為它們?cè)O(shè)置真實(shí)的值, 要么是通過自動(dòng)裝配特性被提供。

    當(dāng)你想確保所有的屬性(或者某一特定類型的所有屬性)都被設(shè)置到bean上面的時(shí)候, 這項(xiàng)特性就很有用了。當(dāng)然,在很多情況下一個(gè)bean類的很多屬性都會(huì)有缺省的值, 或者一些屬性并不會(huì)應(yīng)用到所有的應(yīng)用場(chǎng)景,那么這個(gè)特性的作用就有限了 。 依賴檢查能夠分別對(duì)每一個(gè)bean應(yīng)用或取消應(yīng)用,就像自動(dòng)裝配功能一樣。缺省的是?檢查依賴關(guān)系。 依賴檢查可以以幾種不同的模式處理。在XmlBeanFactory中, 通過bean定義中的?dependency-check?屬性來指定依賴檢查,這個(gè)屬性有以下的值。

    表?3.3.?依賴檢查模式

    模式解釋
    none不進(jìn)行依賴檢查。沒有指定值的bean屬性僅僅是沒有設(shè)值。
    simple對(duì)基本類型和集合(除了合作者外,比如其他的bean,所有東西)進(jìn)行依賴檢查。
    object對(duì)合作者進(jìn)行依賴檢查。
    all對(duì)合作者,基本類型和集合都進(jìn)行依賴檢查。

    3.4.?自定義bean的本質(zhì)特征

    3.4.1.?生命周期接口

    Spring提供了一些標(biāo)志接口,用來改變BeanFactory中的bean的行為。 它們包括InitializingBean和DisposableBean。 實(shí)現(xiàn)這些接口將會(huì)導(dǎo)致BeanFactory調(diào)用前一個(gè)接口的afterPropertiesSet()方法, 調(diào)用后一個(gè)接口destroy()方法,從而使得bean可以在初始化和析構(gòu)后做一些特定的動(dòng)作。

    在內(nèi)部,Spring使用BeanPostProcessors?來處理它能找到的標(biāo)志接口以及調(diào)用適當(dāng)?shù)姆椒ā?如果你需要自定義的特性或者其他的Spring沒有提供的生命周期行為, 你可以實(shí)現(xiàn)自己的?BeanPostProcessor。關(guān)于這方面更多的內(nèi)容可以看這里:?第?3.7?節(jié) “使用BeanPostprocessors定制bean”。

    所有的生命周期的標(biāo)志接口都在下面敘述。在附錄的一節(jié)中,你可以找到相應(yīng)的圖, 展示了Spring如何管理bean;那些生命周期的特性如何改變你的bean的本質(zhì)特征以及它們?nèi)绾伪还芾怼?/p>

    3.4.1.1.?InitializingBean /?init-method

    實(shí)現(xiàn)org.springframework.beans.factory.InitializingBean?接口允許一個(gè)bean在它的所有必須的屬性被BeanFactory設(shè)置后, 來執(zhí)行初始化的工作。InitializingBean接口僅僅制定了一個(gè)方法:

    * Invoked by a BeanFactory after it has set all bean properties supplied* (and satisfied BeanFactoryAware and ApplicationContextAware).* <p>This method allows the bean instance to perform initialization only* possible when all bean properties have been set and to throw an* exception in the event of misconfiguration.* @throws Exception in the event of misconfiguration (such* as failure to set an essential property) or if initialization fails.*/void afterPropertiesSet() throws Exception;

    注意:通常InitializingBean接口的使用是能夠避免的(而且不鼓勵(lì),因?yàn)闆]有必要把代碼同Spring耦合起來)。Bean的定義支持指定一個(gè)普通的初始化方法。在使用XmlBeanFactory的情況下,可以通過指定init-method屬性來完成。 舉例來說,下面的定義:

    <bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>public class ExampleBean {public void init() {// do some initialization work} }

    同下面的完全一樣:

    <bean id="exampleInitBean" class="examples.AnotherExampleBean"/>public class AnotherExampleBean implements InitializingBean {public void afterPropertiesSet() {// do some initialization work} }

    但卻不把代碼耦合于Spring。

    3.4.1.2.?DisposableBean /?destroy-method

    實(shí)現(xiàn)org.springframework.beans.factory.DisposableBean接口允許一個(gè)bean, 可以在包含它的BeanFactory銷毀的時(shí)候得到一個(gè)回調(diào)。DisposableBean也只指定了一個(gè)方法:

    /*** Invoked by a BeanFactory on destruction of a singleton.* @throws Exception in case of shutdown errors.* Exceptions will get logged but not rethrown to allow* other beans to release their resources too.*/void destroy() throws Exception;

    注意:通常DisposableBean接口的使用能夠避免的(而且是不鼓勵(lì)的,因?yàn)樗槐匾貙⒋a耦合于Spring)。 Bean的定義支持指定一個(gè)普通的析構(gòu)方法。在使用XmlBeanFactory使用的情況下,它是通過?destroy-method屬性完成。 舉例來說,下面的定義:

    <bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="destroy"/>public class ExampleBean {public void cleanup() {// do some destruction work (like closing connection)} }

    同下面的完全一樣:

    <bean id="exampleInitBean" class="examples.AnotherExampleBean"/>public class AnotherExampleBean implements DisposableBean {public void destroy() {// do some destruction work} }

    但卻不把代碼耦合于Spring。

    重要的提示:當(dāng)以portotype模式部署一個(gè)bean的時(shí)候,bean的生命周期將會(huì)有少許的變化。 通過定義,Spring無法管理一個(gè)non-singleton/prototype bean的整個(gè)生命周期, 因?yàn)楫?dāng)它創(chuàng)建之后,它被交給客戶端而且容器根本不再留意它了。 當(dāng)說起non-singleton/prototype bean的時(shí)候,你可以把Spring的角色想象成“new”操作符的替代品。 從那之后的任何生命周期方面的事情都由客戶端來處理。BeanFactory中bean的生命周期將會(huì)在第?3.4.1?節(jié) “生命周期接口”?一節(jié)中有更詳細(xì)的敘述?.

    3.4.2.?了解自己

    3.4.2.1.?BeanFactoryAware

    對(duì)于實(shí)現(xiàn)了org.springframework.beans.factory.BeanFactoryAware接口的類, 當(dāng)它被BeanFactory創(chuàng)建后,它會(huì)擁有一個(gè)指向創(chuàng)建它的BeanFactory的引用。

    public interface BeanFactoryAware {/*** Callback that supplies the owning factory to a bean instance.* <p>Invoked after population of normal bean properties but before an init* callback like InitializingBean's afterPropertiesSet or a custom init-method.* @param beanFactory owning BeanFactory (may not be null).* The bean can immediately call methods on the factory.* @throws BeansException in case of initialization errors* @see BeanInitializationException*/void setBeanFactory(BeanFactory beanFactory) throws BeansException; }

    這允許bean可以以編程的方式操控創(chuàng)建它們的BeanFactory, 既可以直接使用?org.springframework.beans.factory.BeanFactory接口, 也可以將引用強(qiáng)制將類型轉(zhuǎn)換為已知的子類型從而獲得更多的功能。這個(gè)特性主要用于編程式地取得其他bean。 雖然在一些場(chǎng)景下這個(gè)功能是有用的,但是一般來說它應(yīng)該避免使用,因?yàn)樗勾a與Spring耦合在一起, 而且也不遵循反向控制的風(fēng)格(合作者應(yīng)當(dāng)作屬性提供給bean)。

    3.4.2.2.?BeanNameAware

    如果一個(gè)bean實(shí)現(xiàn)了org.springframework.beans.factory.BeanNameAware接口, 并且被部署到一個(gè) BeanFactory中,那么BeanFactory就會(huì)通過這個(gè)接口來調(diào)用bean,以便通知這個(gè)bean它被部署的id?。 這個(gè)回調(diào)發(fā)生在普通的bean屬性設(shè)置之后,在初始化回調(diào)之前,比如InitializingBean的afterPropertiesSet方法(或者自定義的init- method)。

    3.4.3.?FactoryBean

    接口org.springframework.beans.factory.FactoryBean?一般由本身是工廠類的對(duì)象實(shí)現(xiàn)。BeanFactory接口提供了三個(gè)方法:

    • Object getObject(): 必須返回一個(gè)這個(gè)工廠類創(chuàng)建的對(duì)象實(shí)例。這個(gè)實(shí)例可以是共享的(取決于這個(gè)工廠返回的是singleton還是prototype)。

    • boolean isSingleton(): 如果Factory返回的對(duì)象是singleton,返回true,否則返回false。

    • Class getObjectType(): 返回getObject()方法返回的對(duì)象的類型,如果類型不是預(yù)先知道的,則返回null。

    3.5.?子bean定義

    一個(gè)bean定義可能會(huì)包含大量的配置信息,包括容器相關(guān)的信息(比如初始化方法,靜態(tài)工廠方法名等等)以及構(gòu)造函數(shù)參數(shù)和屬性的值。一個(gè)子bean定義是一個(gè)能夠從父bean定義繼承配置數(shù)據(jù)的bean定義。 它可以覆蓋一些值,或者添加一些其他需要的值。使用父和子的bean定義可以節(jié)省很多的輸入工作。實(shí)際上,這就是一種模版形式。

    當(dāng)以編程的方式使用一個(gè)BeanFactory,子bean定義用ChildBeanDefinition類表示。 大多數(shù)的用戶從來不需要以這個(gè)方式使用它們,而是在類似XmlBeanFactory的BeanFactory 中以聲明的方式配置bean定義。在一個(gè)XmlBeanFactory的bean定義中,使用parent屬性指出一個(gè)子bean定義,而父bean則作為這個(gè)屬性的值。

    <bean id="inheritedTestBean" class="org.springframework.beans.TestBean"><property name="name"><value>parent</value></property><property name="age"><value>1</value></property> </bean><bean id="inheritsWithDifferentClass" class="org.springframework.beans.DerivedTestBean"parent="inheritedTestBean" init-method="initialize"><property name="name"><value>override</value></property><!-- age should inherit value of 1 from parent --></bean>

    如果子bean定義沒有指定class屬性,將使用父定義的class屬性,當(dāng)然也可以覆蓋它。 在后面一種情況中,子bean的class屬性值必須同父bean的兼容,也就是它必須能夠接受父親的屬性值。

    一個(gè)子bean定義可以從父親處繼承構(gòu)造函數(shù)參數(shù),屬性值以及方法,并且可以選擇增加新的值。 如果init-method,destroy-method和/或靜態(tài)factory-method被指定了,它們就會(huì)覆蓋父親相應(yīng)的設(shè)置。

    剩余的設(shè)置將?總是?從子定義處得到:?依賴,?自動(dòng)裝配模式,?依賴檢查,?singleton,?延遲初始化。

    在下面的例子中父定義并沒有指定class屬性:

    <bean id="inheritedTestBeanWithoutClass"><property name="name"><value>parent</value></property><property name="age"><value>1</value></property> </bean><bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean"parent="inheritedTestBeanWithoutClass" init-method="initialize"><property name="name"><value>override</value></property><!-- age should inherit value of 1 from parent --> </bean>

    這個(gè)父bean就無法自己實(shí)例化;它實(shí)際上僅僅是一個(gè)純模版或抽象bean,充當(dāng)子定義的父定義。 若要嘗試單獨(dú)使用這樣的父bean(比如將它作為其他bean的ref屬性而引用,或者直接使用這個(gè)父 bean的id調(diào)用getBean()方法),將會(huì)導(dǎo)致一個(gè)錯(cuò)誤。同樣地,容器內(nèi)部的preInstantiateSingletons方法會(huì)完全忽略這種既沒有parent屬性也沒有class屬性的bean定義,因?yàn)樗鼈兪遣煌暾摹?/p>

    特別注意:這里并沒有辦法顯式地聲明一個(gè)bean定義為抽象的。 如果一個(gè)bean確實(shí)有一個(gè)class屬性定義,那么它就能夠被實(shí)例化。而且要注意 XmlBeanFactory默認(rèn)地將會(huì)預(yù)實(shí)例化所有的singleton的bean。 因此很重要的一點(diǎn)是:如果你有一個(gè)(父)bean定義指定了class屬性,而你又想僅僅把它當(dāng)作模板使用, 那么你必須保證將lazy-init屬性設(shè)置為true(或者將bean標(biāo)記為non-singleton),否則 XmlBeanFactory(以及其他可能的容器)將會(huì)預(yù)實(shí)例化它。

    3.6.?BeanFactory之間的交互

    BeanFactory本質(zhì)上不過是高級(jí)工廠的接口,它維護(hù)不同bean和它們所依賴的bean的注冊(cè)。 BeanFactory使得你可以利用 bean工廠讀取和訪問bean定義。 當(dāng)你使用BeanFactory的時(shí)候,你可以象下面一樣創(chuàng)建并且讀入一些XML格式的bean定義:

    InputStream is = new FileInputStream("beans.xml"); XmlBeanFactory factory = new XmlBeanFactory(is);

    基本上這就足夠了。使用getBean(String)你可以取得你的bean的實(shí)例。 如果你將它定義為一個(gè)singleton(缺省的)你將會(huì)得到同一個(gè)bean的引用, 如果你將singleton設(shè)置為false,那么你將會(huì)每次得到一個(gè)新的實(shí)例。 在客戶端的眼里BeanFactory是驚人的簡(jiǎn)單。BeanFactory接口僅僅為客戶端調(diào)用提供了5個(gè)方法:

    • boolean containsBean(String): 如果BeanFactory包含一個(gè)與所給名稱匹配的bean定義,則返回true

    • Object getBean(String): 返回一個(gè)以所給名字注冊(cè)的bean的實(shí)例。返回一個(gè)singleton的共享的實(shí)例還是一個(gè)新創(chuàng)建的實(shí)例, 這取決于bean在BeanFactory配置中如何被配置的。一個(gè)BeansException將會(huì)在下面兩種情況中拋出:bean沒有被找到(在這種情況下,拋出的是NoSuchBeanDefinitionException),或者在實(shí)例化和準(zhǔn)備bean的時(shí)候發(fā)生異常

    • Object getBean(String,Class): 返回一個(gè)以給定名字注冊(cè)的bean。返回的bean將會(huì)被強(qiáng)制類型轉(zhuǎn)換成給定的Class。 如果bean不能被類型轉(zhuǎn)換,相應(yīng)的異常將會(huì)被拋出(BeanNotOfRequiredTypeException)。 此外getBean(String)的所有規(guī)則也同樣適用這個(gè)方法(同上)

    • boolean isSingleton(String): 判斷以給定名字注冊(cè)的bean定義是一個(gè)singleton還是一個(gè)prototype。 如果與給定名字相應(yīng)的bean定義沒有被找到,將會(huì)拋出一個(gè)異常(NoSuchBeanDefinitionException)

    • String[] getAliases(String):如果給定的bean名字在bean定義中有別名,則返回這些別名

    3.6.1.?獲得一個(gè)FactoryBean而不是它生成的bean

    有些時(shí)候我們需要向BeanFactory請(qǐng)求實(shí)際的FactoryBean實(shí)例本身, 而不是它生產(chǎn)出來的bean。在調(diào)用BeanFactory (包括ApplicationContext)的getBean方法的時(shí)候, 在傳入的參數(shù)bean id前面加一個(gè)“&”符號(hào),就可以做到這一點(diǎn)。所以,對(duì)于一個(gè)id為getBean的FactoryBean, 在BeanFactory上調(diào)用getBean("myBean")將會(huì)返回FactoryBean的產(chǎn)品,而調(diào)用getBean("&myBean")將會(huì)返回這個(gè)FactoryBean實(shí)例本身。

    3.7.?使用BeanPostprocessors定制bean

    一個(gè)bean post-processor是一個(gè)實(shí)現(xiàn)了org.springframework.beans.factory.config.BeanPostProcessor的類,它包含兩個(gè)回調(diào)方法。當(dāng)這樣的一個(gè)類作為BeanFactory的post-processor注冊(cè)時(shí),對(duì)于這個(gè)BeanFactory創(chuàng)建的每一個(gè)bean實(shí)例,在任何初始化方法(afterPropertiesSet和用init-method屬性聲明的方法)被調(diào)用之前和之后,post-processor將會(huì)從BeanFactory分別得到一個(gè)回調(diào)。post-processor可以對(duì)這個(gè)bean做任何操作事情,包括完全忽略這個(gè)回調(diào)。一個(gè)bean post-processor通常用來檢查標(biāo)記接口,或者做一些諸如將一個(gè)bean包裝成一個(gè)proxy的事情。一些Spring的助手類就是作為bean post-processor而實(shí)現(xiàn)的。

    有一點(diǎn)很重要,BeanFactory和ApplicationContext對(duì)待bean post-processor有少許不同。 一個(gè)ApplicationContext會(huì)自動(dòng)監(jiān)測(cè)到任何部署在它之上的實(shí)現(xiàn)了?BeanPostProcessor接口的bean, 并且把它們作為post-processor注冊(cè),然后factory就會(huì)在bean創(chuàng)建的時(shí)候恰當(dāng)?shù)卣{(diào)用它們。 部署一個(gè)post-processor同部屬一個(gè)其他的bean并沒有什么區(qū)別。而另一方面,當(dāng)使用普通的BeanFactory的時(shí)候, 作為post-processor的bean必須通過類似下面的代碼來手動(dòng)地顯式地注冊(cè):

    ConfigurableBeanFactory bf = new .....; // create BeanFactory... // now register some beans // now register any needed BeanPostProcessors MyBeanPostProcessor pp = new MyBeanPostProcessor(); bf.addBeanPostProcessor(pp);// now start using the factory...

    因?yàn)槭止さ淖?cè)不是很方便,而且ApplicationContext是BeanFactory功能上擴(kuò)展,所以通常建議當(dāng)需要post-processor的時(shí)候最好使用ApplicationContext。

    3.8.?使用BeanFactoryPostprocessors定制bean工廠

    一個(gè)bean factory post-processor是一個(gè)實(shí)現(xiàn)了org.springframework.beans.factory.config.BeanFactoryPostProcessor接口的類。它將會(huì)被手動(dòng)執(zhí)行(BeanFactory的時(shí)候)或自動(dòng)執(zhí)行(ApplicationContext的時(shí)候),在BeanFactory構(gòu)造出來后, 對(duì)整個(gè)BeanFactory做某種修改。Spring包含很多已存在的bean factory post-processor, 比如PropertyResourceConfigurer和PropertyPlaceHolderConfigurer(這兩個(gè)將在下面介紹),以及BeanNameAutoProxyCreator對(duì)其他bean進(jìn)行事務(wù)包裝或者用其他的proxy進(jìn)行包裝,將會(huì)在后面敘述。BeanFactoryPostProcessor也能用來添加自定義的editor(可參見第?4.3.2?節(jié) “內(nèi)建的(PropertyEditors)和類型轉(zhuǎn)換 ”)。

    在BeanFactory中,使用BeanFactoryPostProcessor是手動(dòng)的,將類似于下面:

    XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml")); // create placeholderconfigurer to bring in some property // values from a Properties file PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer(); cfg.setLocation(new FileSystemResource("jdbc.properties")); // now actually do the replacement cfg.postProcessBeanFactory(factory);

    ApplicationContext將會(huì)檢測(cè)它所部署的實(shí)現(xiàn)了BeanFactoryPostProcessor?接口的bean,然后在適當(dāng)?shù)臅r(shí)候?qū)⑺鼈冏鳛閎ean factory post-processor而使用。部署這些post-processor與部署其他的bean并沒有什么區(qū)別。

    因?yàn)檫@個(gè)手動(dòng)的步驟并不方便,而且ApplicationContext是BeanFactory的功能擴(kuò)展,所以當(dāng)需要使用bean factory post-processor的時(shí)候通常建議使用ApplicationContext

    3.8.1.?PropertyPlaceholderConfigurer

    PropertyPlaceholderConfigurer作為一個(gè)bean factory post-processor實(shí)現(xiàn),可以用來將BeanFactory定義中的屬性值放置到另一個(gè)單獨(dú)的Java Properties格式的文件中。 這使得用戶不用對(duì)BeanFactory的主XML定義文件進(jìn)行復(fù)雜和危險(xiǎn)的修改,就可以定制一些基本的屬性(比如說數(shù)據(jù)庫的urls,用戶名和密碼)。

    考慮一個(gè)BeanFactory定義的片斷,里面用占位符定義了DataSource:

    在下面這個(gè)例子中,定義了一個(gè)datasource,并且我們會(huì)在一個(gè)外部Porperties文件中配置一些相關(guān)屬性。 在運(yùn)行時(shí),我們?yōu)锽eanFactory提供一個(gè)PropertyPlaceholderConfigurer,它將用Properties文件中的值替換掉這個(gè)datasource的屬性值:

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"><property name="driverClassName"><value>${jdbc.driverClassName}</value></property><property name="url"><value>${jdbc.url}</value></property><property name="username"><value>${jdbc.username}</value></property><property name="password"><value>${jdbc.password}</value></property> </bean>

    真正的值來自于另一個(gè)Properties格式的文件:

    jdbc.driverClassName=org.hsqldb.jdbcDriver jdbc.url=jdbc:hsqldb:hsql://production:9002 jdbc.username=sa jdbc.password=root

    如果要在BeanFactory中使用,bean factory post-processor必須手動(dòng)運(yùn)行:

    XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml")); PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer(); cfg.setLocation(new FileSystemResource("jdbc.properties")); cfg.postProcessBeanFactory(factory);

    注意,ApplicationContext能夠自動(dòng)辨認(rèn)和應(yīng)用在其上部署的實(shí)現(xiàn)了BeanFactoryPostProcessor的bean。這就意味著,當(dāng)使用ApplicationContext的時(shí)候應(yīng)用PropertyPlaceholderConfigurer會(huì)非常的方便。由于這個(gè)原因,建議想要使用這個(gè)或者其他bean factory postprocessor的用戶使用ApplicationContext代替BeanFactroy。

    PropertyPlaceHolderConfigurer不僅僅在你指定的Porperties文件中查找屬性, 如果它在其中沒有找到你想使用的屬性,它還會(huì)在Java的系統(tǒng)properties中查找。 這個(gè)行為能夠通過設(shè)置配置中的systemPropertiesMode?屬性來定制。這個(gè)屬性有三個(gè)值, 一個(gè)讓配置總是覆蓋,一個(gè)讓它永不覆蓋,一個(gè)讓它僅在properties文件中找不到的時(shí)候覆蓋。 請(qǐng)參考 PropertiesPlaceholderConfigurer的JavaDoc獲得更多信息。

    3.8.2.??PropertyOverrideConfigurer

    另一個(gè)bean factory post-processor,PropertyOverrideConfigurer,類似于PropertyPlaceholderConfigurer,但是與后者相比,前者對(duì)于bean屬性可以有缺省值或者根本沒有值。如果起覆蓋作用的 Properties文件沒有某個(gè)bean屬性的內(nèi)容,那么缺省的上下文定義將被使用。

    注意:bean 工廠的定義并?不會(huì)意識(shí)到被覆蓋,所以僅僅察看XML定義文件并不能立刻明顯地知道覆蓋配置是否被使用了。在有多個(gè)PorpertyOverrideConfigurer對(duì)用一個(gè)bean屬性定義了不同的值的時(shí)候,最后一個(gè)將取勝(取決于覆蓋的機(jī)制)。

    Properties文件的一行配置應(yīng)該是如下的格式:

    beanName.property=value

    一個(gè)properties文件的例子將會(huì)是下面這樣的:

    dataSource.driverClassName=com.mysql.jdbc.Driver dataSource.url=jdbc:mysql:mydb

    這個(gè)例子可以用在包含dataSource的bean的BeanFactory中,這個(gè)bean有driverurl屬性。

    3.9.?注冊(cè)附加的定制PropertyEditor

    當(dāng)用字符串值設(shè)置bean的屬性時(shí),BeanFactory實(shí)質(zhì)上使用了標(biāo)準(zhǔn)的JavaBeans 的PropertyEditor將這些String轉(zhuǎn)換為屬性的復(fù)雜類型。Spring預(yù)先注冊(cè)了很多定制的PropertyEditor(比如,將一個(gè)字符串表示的classname轉(zhuǎn)換成真正的Class對(duì)象)。另外,一個(gè)類的PropertyEditor如果被恰當(dāng)?shù)孛⑶曳旁谂c它提供支持的類同一個(gè)包內(nèi),那么Java標(biāo)準(zhǔn)的JavaBeans PropertyEditor查找機(jī)制就會(huì)自動(dòng)找到這個(gè)PropertyEditor。

    如果需要注冊(cè)其他定制的PropertyEditor,這里也有幾個(gè)可用的機(jī)制。

    最手動(dòng)的方法,也是通常不方便和不推薦的,就是簡(jiǎn)單地使用ConfigurableBeanFactory接口的registerCustomEditor()方法,假定你已經(jīng)有一個(gè)BeanFactory引用。

    比較方便的機(jī)制是,使用一個(gè)特殊的叫CustomEditorConfigurer的bean factory post-precessor。盡管bean factory post-processor能夠和BeanFactory半手動(dòng)地使用,但是它有一個(gè)嵌套的屬性設(shè)置。所以強(qiáng)烈建議,就象這里描述的,它能夠和ApplicationContxt一起使用,這樣它就可以以其它bean的方式被部署,并且被自動(dòng)監(jiān)測(cè)和應(yīng)用。

    3.10.?介紹ApplicationContext

    beans包提供了以編程的方式管理和操控bean的基本功能,而context包增加了ApplicationContext,它以一種更加面向框架的方式增強(qiáng)了BeanFactory的功能。多數(shù)用戶可以以一種完全的聲明式方式來使用ApplicationContext,甚至不用去手動(dòng)創(chuàng)建它,但是卻去依賴象ContextLoader的支持類,在J2EE的的web應(yīng)用的啟動(dòng)進(jìn)程中用它啟動(dòng)ApplicationContext。當(dāng)然,這種情況下還是可以以編程的方式創(chuàng)建一個(gè)ApplicationContext。

    Context包的基礎(chǔ)是位于org.springframework.context包中的ApplicationContext接口。它是由BeanFactory接口集成而來,提供BeanFactory所有的功能。為了以一種更向面向框架的方式工作,context包使用分層和有繼承關(guān)系的上下文類,包括:

    • MessageSource, 提供對(duì)i18n消息的訪問

    • 資源訪問, 比如URL和文件

    • 事件傳遞給實(shí)現(xiàn)了ApplicationListener接口的bean

    • 載入多個(gè)(有繼承關(guān)系)上下文類,使得每一個(gè)上下文類都專注于一個(gè)特定的層次,比如應(yīng)用的web層

    因?yàn)锳pplicationContext包括了BeanFactory所有的功能,所以通常建議先于BeanFactory使用,除了有限的一些場(chǎng)合比如在一個(gè)Applet中,內(nèi)存的消耗是關(guān)鍵的,每kb字節(jié)都很重要。接下來的章節(jié)將敘述ApplicationContext在BeanFactory的基本能力上增建的功能。

    3.11.?ApplicationContext中增加的功能

    正如上面說明的那樣,ApplicationContext有幾個(gè)區(qū)別于BeanFactory的特性。讓我們一個(gè)一個(gè)地討論它們。

    3.11.1.?使用MessageSource

    ApplicationContext接口繼承MessageSource接口,所以提供了messaging功能(i18n或者國際化)。同NestingMessageSource一起使用,就能夠處理分級(jí)的信息,這些是Spring提供的處理信息的基本接口。讓我們很快瀏覽一下這里定義的方法:

    • String getMessage (String code, Object[] args, String default, Locale loc):這個(gè)方法是從MessageSource取得信息的基本方法。如果對(duì)于指定的locale沒有找到信息,則使用默認(rèn)的信息。傳入的參數(shù)args被用來代替信息中的占位符,這個(gè)是通過Java標(biāo)準(zhǔn)類庫的MessageFormat實(shí)現(xiàn)的。

    • String getMessage (String code, Object[] args, Locale loc):本質(zhì)上和上一個(gè)方法是一樣的,除了一點(diǎn)區(qū)別:沒有默認(rèn)值可以指定;如果信息找不到,就會(huì)拋出一個(gè)NoSuchMessageException。

    • String getMessage(MessageSourceResolvable resolvable, Locale locale):上面兩個(gè)方法使用的所有屬性都是封裝到一個(gè)叫做MessageSourceResolvable的類中,你可以通過這個(gè)方法直接使用它。

    當(dāng)ApplicationContext被加載的時(shí)候,它會(huì)自動(dòng)查找在context中定義的MessageSource bean。這個(gè)bean必須叫做messageSource。如果找到了這樣的一個(gè)bean,所有對(duì)上述方法的調(diào)用將會(huì)被委托給找到的message source。如果沒有找到message source,ApplicationContext將會(huì)嘗試查它的父親是否包含這個(gè)名字的bean。如果有,它將會(huì)把找到的bean作為MessageSource。如果它最終沒有找到任何的信息源,一個(gè)空的StaticMessageSource將會(huì)被實(shí)例化,使它能夠接受上述方法的調(diào)用。

    Spring目前提供了兩個(gè)MessageSource的實(shí)現(xiàn)。它們是ResourceBundleMessageSource和StaticMessageSource。兩個(gè)都實(shí)現(xiàn)了NestingMessageSource以便能夠嵌套地解析信息。StaticMessageSource很少被使用,但是它提供以編程的方式向source增加信息。ResourceBundleMessageSource用得更多一些,我們將提供它的一個(gè)例子:

    <beans><bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"><property name="basenames"><list><value>format</value><value>exceptions</value><value>windows</value></list></property></bean> </beans>

    這段配置假定你在classpath有三個(gè)resource bundle,分別叫做fformat,exceptions和windows。使用JDK通過ResourceBundle解析信息的標(biāo)準(zhǔn)方式,任何解析信息的請(qǐng)求都會(huì)被處理。TODO:舉一個(gè)例子

    3.11.2.?事件傳遞

    ApplicationContext中的事件處理是通過ApplicationEvent類和ApplicationListener接口來提供的。如果上下文中部署了一個(gè)實(shí)現(xiàn)了ApplicationListener接口的bean,每次一個(gè)ApplicationEvent發(fā)布到ApplicationContext時(shí),那個(gè)bean就會(huì)被通知。實(shí)質(zhì)上,這是標(biāo)準(zhǔn)的Observer設(shè)計(jì)模式。Spring提供了三個(gè)標(biāo)準(zhǔn)事件:

    表?3.4.?內(nèi)置事件

    事件解釋
    ContextRefreshedEvent當(dāng)ApplicationContext已經(jīng)初始化或刷新后發(fā)送的事件。這里初始化意味著:所有的bean被裝載,singleton被預(yù)實(shí)例化,以及ApplicationContext已準(zhǔn)備好
    ContextClosedEvent當(dāng)使用ApplicationContext的close()方法結(jié)束上下文的時(shí)候發(fā)送的事件。這里結(jié)束意味著:singleton被銷毀
    RequestHandledEvent一個(gè)與web相關(guān)的事件,告訴所有的bean一個(gè)HTTP請(qǐng)求已經(jīng)被響應(yīng)了(這個(gè)事件將會(huì)在一個(gè)請(qǐng)求結(jié)束被發(fā)送)。注意,這個(gè)事件只能應(yīng)用于使用了Spring的DispatcherServlet的web應(yīng)用

    同樣也可以實(shí)現(xiàn)自定義的事件。通過調(diào)用ApplicationContext的publishEvent()方法,并且指定一個(gè)參數(shù),這個(gè)參數(shù)是你自定義的事件類的一個(gè)實(shí)例。我們來看一個(gè)例子。首先是ApplicationContext:

    <bean id="emailer" class="example.EmailBean"><property name="blackList"><list><value>black@list.org</value><value>white@list.org</value><value>john@doe.org</value></list></property> </bean><bean id="blackListListener" class="example.BlackListNotifier"><property name="notificationAddress"><value>spam@list.org</value></property> </bean>

    然后是實(shí)際的bean:

    public class EmailBean implements ApplicationContextAware {/** the blacklist */private List blackList;public void setBlackList(List blackList) {this.blackList = blackList;}public void setApplicationContext(ApplicationContext ctx) {this.ctx = ctx;}public void sendEmail(String address, String text) {if (blackList.contains(address)) {BlackListEvent evt = new BlackListEvent(address, text);ctx.publishEvent(evt);return;}// send email} }public class BlackListNotifier implement ApplicationListener {/** notification address */private String notificationAddress;public void setNotificationAddress(String notificationAddress) {this.notificationAddress = notificationAddress;}public void onApplicationEvent(ApplicationEvent evt) {if (evt instanceof BlackListEvent) {// notify appropriate person}} }

    當(dāng)然,這個(gè)特定的例子或許可以用更好的方式實(shí)現(xiàn)(或許使用AOP特性),但是它用來說明基本的事件機(jī)制是足夠了。

    3.11.3.?在Spring中使用資源

    很多應(yīng)用程序都需要訪問資源。資源可以包括文件,以及象web頁面或NNTP newsfeeds的東西。Spring提供了一個(gè)清晰透明的方案,以一種協(xié)議無關(guān)的方式訪問資源。ApplicationContext接口包含一個(gè)方法(getResource(String))負(fù)責(zé)這項(xiàng)工作。

    Resource類定義了幾個(gè)方法,這幾個(gè)方法被所有的Resource實(shí)現(xiàn)所共享:

    表?3.5.?資源功能

    方法解釋
    getInputStream()用InputStream打開資源,并返回這個(gè)InputStream。
    exists()檢查資源是否存在,如果不存在返回false
    isOpen()如果這個(gè)資源不能打開多個(gè)流將會(huì)返回true。因?yàn)槌嘶谖募馁Y源,一些資源不能被同事多次讀取,它們就會(huì)返回false。
    getDescription()返回資源的描述,通常是全限定文件名或者實(shí)際的URL。

    Spring提供了幾個(gè)Resource的實(shí)現(xiàn)。它們都需要一個(gè)String表示的資源的實(shí)際位置。依據(jù)這個(gè)String,Spring將會(huì)自動(dòng)為你選擇正確的Resource實(shí)現(xiàn)。當(dāng)向ApplicationContext請(qǐng)求一個(gè)資源時(shí),Spring首先檢查你指定的資源位置,尋找任何前綴。根據(jù)不同的ApplicationContext的實(shí)現(xiàn),不同的Resource實(shí)現(xiàn)可被使用的。Resource最好是使用ResourceEditor來配置,比如XmlBeanFactory。

    3.12.?在ApplicationContext中定制行為

    BeanFactory已經(jīng)提供了許多機(jī)制來控制部署在其中的bean的生命周期(比如象InitializingBean或DisposableBean的標(biāo)志接口,它們的配置效果和XmlBeanFactory配置中的init-method和destroy-method屬性以及bean post-processor是相同的。在ApplicationContext中,這些也同樣可以工作,但同時(shí)也增加了一些用于定制bean和容器行為的機(jī)制。

    3.12.1.?ApplicationContextAware標(biāo)記接口

    所有BeanFactory中可用的標(biāo)志接口這里也可以使用。ApplicationContext又增加了一個(gè)bean可以實(shí)現(xiàn)的標(biāo)志接口:org.springframework.context.ApplicationContextAware。如果一個(gè)bean實(shí)現(xiàn)了這個(gè)接口并且被部署到了context中,當(dāng)這個(gè)bean創(chuàng)建的時(shí)候,將使用這個(gè)接口的setApplicationContext()方法回調(diào)這個(gè)bean,為這個(gè)bean提供一個(gè)指向當(dāng)前上下文的引用,這個(gè)引用將被存儲(chǔ)起來以便bean以后與上下文發(fā)生交互。

    3.12.2.?BeanPostProcessor

    Bean post-processor(實(shí)現(xiàn)了org.springframework.beans.factory.config.BeanPostProcessor的java類)在上面已經(jīng)敘述過了。還是值得再次提到,post-processor在ApplicationContext中使用要比在普通的BeanFactory中使用方便得多。在一個(gè)ApplicationContext中,一個(gè)實(shí)現(xiàn)了上述標(biāo)記接口的bean將會(huì)被自動(dòng)查找,并且作為一個(gè)bean post-processor被注冊(cè),在創(chuàng)建工廠中的每一個(gè)bean時(shí)都會(huì)被適當(dāng)?shù)卣{(diào)用。

    3.12.3.?BeanFactoryPostProcessor

    Bean factory post-processor(實(shí)現(xiàn)了org.springframework.beans.factory.config.BeanFactoryPostProcessor接口的java類)在前面已經(jīng)介紹過。這里也值得再提一下,bean factory post-processor在ApplicationContext中使用也要比在普通的BeanFactory中使用方便得多。在一個(gè)ApplicationContext中,一個(gè)實(shí)現(xiàn)了上述標(biāo)記接口的bean將會(huì)被自動(dòng)查找,并且作為一個(gè)bean factory post-processor被注冊(cè),在適當(dāng)?shù)臅r(shí)候被調(diào)用。

    3.12.4.?PropertyPlaceholderConfigurer

    PropertyPlaceholderConfigurer在BeanFactory中的使用已經(jīng)敘述過了。這里仍然值得再次提一下,它在ApplicationContext中的使用要更加方便一些,因?yàn)楫?dāng)它像其它bean一樣部署的時(shí)候,上下文會(huì)自動(dòng)識(shí)別和應(yīng)用任何的bean factory post-processor,比如這個(gè)。這時(shí)候就沒有必要手動(dòng)地運(yùn)行它。

    <!-- property placeholder post-processor --> <bean id="placeholderConfig"class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><property name="location"><value>jdbc.properties</value></property> </bean>

    3.13.?注冊(cè)附加的定制PropertyEditors

    正如前面曾提到的,Spring使用標(biāo)準(zhǔn)的JavaBeans的PropertyEditor將字符串形式表示的屬性值轉(zhuǎn)換為屬性真實(shí)的復(fù)雜的類型。CustomEditorConfigurer,這個(gè)bean factory post-processor可以使ApplicationContext很方便地增加對(duì)額外的PropertyEditor的支持。

    考慮一個(gè)用戶類ExoticType,以及另外一個(gè)需要ExoticType作為屬性的DependsOnExoticType類:

    public class ExoticType {private String name;public ExoticType(String name) {this.name = name;} }public class DependsOnExoticType { private ExoticType type;public void setType(ExoticType type) {this.type = type;} }

    當(dāng)設(shè)置好的時(shí)候,我們希望能夠以字符串的形式指定屬性,同時(shí)背后的PropertyEditor會(huì)將這個(gè)字符串轉(zhuǎn)換為一個(gè)真正的Exotic類型的對(duì)象:

    <bean id="sample" class="example.DependsOnExoticType"><property name="type"><value>aNameForExoticType</value></property> </bean>

    而這個(gè)PorpertyEditor看起來象下面這樣:

    // converts string representation to ExoticType object public class ExoticTypeEditor extends PropertyEditorSupport {private String format;public void setFormat(String format) {this.format = format;}public void setAsText(String text) {if (format != null && format.equals("upperCase")) {text = text.toUpperCase();}ExoticType type = new ExoticType(text);setValue(type);} }

    最后,我們用CustomEditorConfigurer將新的PropertyEditor注冊(cè)到ApplicationContext上,然后ApplicationContext就可以在需要的時(shí)候使用這個(gè)PropertyEditor了:

    <bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer"><property name="customEditors"><map><entry key="example.ExoticType"><bean class="example.ExoticTypeEditor"><property name="format"><value>upperCase</value></property></bean></entry></map></property> </bean>

    3.14.?用方法調(diào)用的返回值來設(shè)置bean的屬性

    有些時(shí)候,需要用容器中另一個(gè)bean的方法返回值來設(shè)置一個(gè)bean的屬性,或者使用其它任意類(不一定是容器中的bean)的靜態(tài)方法的返回值來設(shè)置。此外,有些時(shí)候,需要調(diào)用一個(gè)靜態(tài)或非靜態(tài)的方法來執(zhí)行某些初始化工作。對(duì)于這兩個(gè)目的,可以使用MethodInvokingFactoryBean助手類。它是一個(gè)FactoryBean,可以返回一個(gè)靜態(tài)或非靜態(tài)方法的調(diào)用結(jié)果。

    下面是一個(gè)基于XML的BeanFactory的bean定義的例子,它使用那個(gè)助手類調(diào)用靜態(tài)工廠方法:

    <bean id="myClass" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"><property name="staticMethod"><value>com.whatever.MyClassFactory.getInstance</value></property> </bean>

    下面這個(gè)例子先調(diào)用一個(gè)靜態(tài)方法,然后調(diào)用一個(gè)實(shí)例方法,來獲得一個(gè)Java System的屬性。雖然有點(diǎn)羅嗦,但是可以工作:

    <bean id="sysProps" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"><property name="targetClass"><value>java.lang.System</value></property><property name="targetMethod"><value>getProperties</value></property> </bean> <bean id="javaVersion" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"><property name="targetObject"><ref local="sysProps"/></property><property name="targetMethod"><value>getProperty</value></property><property name="arguments"><list><value>java.version</value></list></property> </bean>

    注意,實(shí)際上這個(gè)類多半用來訪問工廠方法,所以MethodInvokingFactoryBean默認(rèn)以singleton方式進(jìn)行操作。經(jīng)由容器的第一次請(qǐng)求工廠生成對(duì)象將會(huì)引起調(diào)用特定的工廠方法,它的返回值將會(huì)被緩存并且返回供這次請(qǐng)求和以后的請(qǐng)求使用。這個(gè)工廠的一個(gè)內(nèi)部singleton屬性可以被設(shè)置為false,從而導(dǎo)致每次對(duì)象的請(qǐng)求都會(huì)調(diào)用那個(gè)目標(biāo)方法。

    通過設(shè)置targetMethod屬性為一個(gè)靜態(tài)方法名的字符串來指定靜態(tài)目標(biāo)方法,而設(shè)置targetClass為定義靜態(tài)方法的類。或者,通過設(shè)置targetObject屬性目標(biāo)對(duì)象,設(shè)置targetMethod屬性要在目標(biāo)對(duì)象上調(diào)用的方法名,來指定目標(biāo)實(shí)例方法。方法調(diào)用的參數(shù)可以通過設(shè)置args屬性來指定。

    3.15.?從一個(gè)web應(yīng)用創(chuàng)建ApplicationContext

    與BeanFactory總是以編程的方式創(chuàng)建相反,ApplicationContext可以通過使用比如ContextLoader聲明式地被創(chuàng)建。當(dāng)然你也可以用ApplicationContext的任一種實(shí)現(xiàn)來以編程的方式創(chuàng)建它。首先,我們來看看ContextLoader以及它的實(shí)現(xiàn)。

    ContextLoader有兩個(gè)實(shí)現(xiàn):ContextLoaderListener和ContextLoaderServlet。它們兩個(gè)有著同樣的功能,除了listener不能在Servlet 2.2兼容的容器中使用。自從Servelt 2.4規(guī)范,listener被要求在web應(yīng)用啟動(dòng)后初始化。很多2.3兼容的容器已經(jīng)實(shí)現(xiàn)了這個(gè)特性。使用哪一個(gè)取決于你自己,但是如果所有的條件都一樣,你大概會(huì)更喜歡ContextLoaderListener;關(guān)于兼容方面的更多信息可以參照ContextLoaderServlet的JavaDoc。

    你可以象下面這樣用ContextLoaderListener注冊(cè)一個(gè)ApplicationContext:

    <context-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value> </context-param><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener><!-- OR USE THE CONTEXTLOADERSERVLET INSTEAD OF THE LISTENER <servlet><servlet-name>context</servlet-name><servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class><load-on-startup>1</load-on-startup> </servlet> -->

    這個(gè)listener需要檢查contextConfigLocation參數(shù)。如果不存在的話,它將默認(rèn)使用/WEB-INF/applicationContext.xml。如果它存在,它就會(huì)用預(yù)先定義的分隔符(逗號(hào),分號(hào)和空格)分開分割字符串,并將這些值作為應(yīng)用上下文將要搜索的位置。ContextLoaderServlet可以用來替換ContextLoaderListener。這個(gè)servlet像listener那樣使用contextConfigLocation參數(shù)。

    3.16.?粘合代碼和罪惡的singleton

    一個(gè)應(yīng)用中的大多數(shù)代碼最好寫成依賴注射(反向控制)的風(fēng)格,這樣這些代碼就和BeanFactory容器或者ApplicationContext容器無關(guān),它們?cè)诒粍?chuàng)建的時(shí)候從容器處得到自己的依賴,并且完全不知道容器的存在。然而,對(duì)于少量需要與其它代碼粘合的粘合層代碼來說,有時(shí)候就需要以一種singleton(或者類似singleton)的方式來訪問BeanFactory或ApplicationContext。舉例來說,第三方的代碼可能想要(以Class.forName()的方式)直接構(gòu)造一個(gè)新的對(duì)象,卻沒辦法從BeanFactory中得到這些對(duì)象。如果第三方代碼構(gòu)造的對(duì)象只是一個(gè)小的stub或proxy,并且使用singleton方式訪問BeanFactroy/ApplicationContext來獲得真正的對(duì)象,大多數(shù)的代碼(由BeanFactory產(chǎn)生的對(duì)象)仍然可以使用反向控制。因此大多數(shù)的代碼依然不需要知道容器的存在,或者容器是如何被訪問的,并保持和其它代碼解耦,有著所有該有的益處。EJB也可以使用這種stub/proxy方案代理到一個(gè)普通的BeanFactory產(chǎn)生的java實(shí)現(xiàn)的對(duì)象。雖然理想情況下BeanFactory不需要是一個(gè)singleton,但是如果每個(gè)bean使用它自己的non-singleton的BeanFactory,對(duì)于內(nèi)存使用或初始化次數(shù)都是不切實(shí)際。

    另一個(gè)例子,在一個(gè)多層的復(fù)雜的J2EE應(yīng)用中(比如有很多JAR,EJB,以及WAR打包成一個(gè)EAR),每一層都有自己的ApplicationContext定義(有效地組成一個(gè)層次結(jié)構(gòu)),如果頂層只有一個(gè)web-app(WAR)的話,比較好的做法是創(chuàng)建一個(gè)由不同層的XML定義文件組成的組合ApplicationContext。所有的ApplicationContext變體都可以從多個(gè)定義文件以這種方式構(gòu)造出來。但是,如果在頂層有多個(gè)兄弟web-apps,為每一個(gè)web-app創(chuàng)建一個(gè)ApplicationContext,但是每個(gè)ApplicationContext都包含大部分相同的底層的bean定義,這就會(huì)因?yàn)閮?nèi)存使用而產(chǎn)生問題,因?yàn)閯?chuàng)建bean的多個(gè)copy會(huì)花很長(zhǎng)時(shí)間初始化(比如Hibernate SessionFactory),以及其它可能產(chǎn)生的副作用。作為替換,諸如ContextSingletonBeanFactoryLocator和SingletonBeanFactoryLocator的類可以在需要的時(shí)候以有效的singleton方式,加載多個(gè)層次的(比如一個(gè)是另一個(gè)的父親)BeanFactory或ApplicationContext,這些將會(huì)作為web-app ApplicationContext的parents。這樣做的結(jié)果就是,底層的bean定義只在需要的時(shí)候加載并且只被加載一次。

    3.16.1.?使用SingletonBeanFactoryLocator和ContextSingletonBeanFactoryLocator

    你可以查看SingletonBeanFactoryLocator和ContextSingletonBeanFactoryLocator的JavaDoc來獲得詳細(xì)的使用例子。

    正如在EJB那一章提到的,Spring為EJB提供的方便的基類一般使用一個(gè)non-singleton的BeanFactoryLocator實(shí)現(xiàn),這個(gè)可以在需要的時(shí)候被SingletonBeanFactoryLocator和ContextSingletonBeanFactoryLocator替換。



    [1]?See?第?3.3.1?節(jié) “設(shè)置bean的屬性和合作者”


    from:?http://docs.huihoo.com/spring/zh-cn/beans.html

    總結(jié)

    以上是生活随笔為你收集整理的Spring - Java/J2EE Application Framework 应用框架 第 3 章 Beans, BeanFactory和ApplicationContext的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。