原貼地址:http://book.csdn.net/bookfiles/111/1001113461.shtml
一.Spring IOC反轉(zhuǎn)控制 BeanFactory
Spring IoC
設(shè)計的核心是
org.springframework.beans
包,它的設(shè)計目標是與
JavaBean
組件一起使用。這個包通常不是由用戶直接使用,而是由服務(wù)器將其用作其他多數(shù)功能的底層中介。下一個最高級抽象是
BeanFactory
接口,它是工廠設(shè)計模式的實現(xiàn),允許通過名稱創(chuàng)建和檢索對象。
BeanFactory
也可以管理對象之間的關(guān)系。
BeanFactory 支持兩個對象模型。
單態(tài)模型:它提供了具有特定名稱的對象的共享實例,可以在查詢時對其進行檢索。 Singleton 是默認的也是最常用的對象模型,對于無狀態(tài)服務(wù)對象很理想。
原型模型:它確保每次檢索都會創(chuàng)建單獨的對象。在每個用戶都需要自己的對象時,原型模型最適合。
bean 工廠的概念是 Spring 作為 IoC 容器的基礎(chǔ), IoC 將處理事情的責(zé)任從應(yīng)用程序代碼轉(zhuǎn)移到框架。 Spring 框架使用 JavaBean 屬性和配置數(shù)據(jù)來指出必須設(shè)置的依賴關(guān)系。
1 . BeanFactory
BeanFactory 實際上是實例化,配置和管理眾多 bean 的容器。這些 bean 通常會彼此合作,因而它們之間會產(chǎn)生依賴。 BeanFactory 使用的配置數(shù)據(jù)可以反映這些依賴關(guān)系(一些依賴可能不像配置數(shù)據(jù)一樣可見,而是在運行期作為 bean 之間程序交互的函數(shù))。
一個 BeanFactory 可以用接口 org.springframework.beans.factory.BeanFactory 表示,這個接口有多個實現(xiàn)。最常使用的簡單的 BeanFactory 實現(xiàn)是 org.springframework.beans.factory. xml.XmlBeanFactory (這里提醒一下, ApplicationContext 是 BeanFactory 的子類,所以大多數(shù)的用戶更喜歡使用 ApplicationContext 的 XML 形式)。
雖然大多數(shù)情況下,幾乎所有被 BeanFactory 管理的用戶代碼都不需要知道 BeanFactory ,但是 BeanFactory 還是以某種方式實例化。可以使用下面的代碼實例化 BeanFactory 。
InputStream is = new FileInputStream("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(is);
也可以使用下列代碼實例化 BeanFactory 。
ClassPathResource res = new ClassPathResource("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(res);
實例化 BeanFactory 還可以采用如下代碼。
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplication Context(?
???new String[] {"applicationContext.xml", "applicationContext-part2. xml"});
// of course, an ApplicationContext is just a BeanFactory
BeanFactory factory = (BeanFactory) appContext;
很多情況下,用戶代碼不需要實例化 BeanFactory ,因為 Spring 框架代碼會做這件事。例如, Web 層提供支持代碼,在 J2EE Web 應(yīng)用啟動過程中自動載入一個 Spring ApplicationContext 。這個聲明過程在這里描述。
編程操作 BeanFactory 將會在后面提到,下面部分將集中描述 BeanFactory 的配置。
一個最基本的 BeanFactory 配置由一個或多個它所管理的 Bean 定義組成。在一個 XmlBeanFactory 中,根節(jié)點 beans 中包含一個或多個 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>
...
2 . BeanDefinition
一個 XmlBeanFactory 中的 Bean 定義包括的內(nèi)容如下。
classname :這通常是 bean 真正的實現(xiàn)類。但是,如果一個 bean 使用一個靜態(tài)工廠方法所創(chuàng)建,而不是被普通的構(gòu)造函數(shù)創(chuàng)建,那么這實際上就是工廠類的 classname 。
bean 行為配置元素:它聲明這個 bean 在容器的行為方式(比如 prototype 或 singleton 、自動裝配模式、依賴檢查模式、初始化和析構(gòu)方法)。
構(gòu)造函數(shù)的參數(shù)和新創(chuàng)建 bean 需要的屬性:舉一個例子,一個管理連接池的 bean 使用的連接數(shù)目(即可以指定為一個屬性,也可以作為一個構(gòu)造函數(shù)參數(shù))或者池的大小限制。
和這個 bean 工作相關(guān)的其他 bean :比如它的合作者(同樣可以作為屬性或者構(gòu)造函數(shù)的參數(shù)),這個也被叫做依賴。
上面列出的概念直接轉(zhuǎn)化為組成 bean 定義的一組元素。這些元素在表 6-1 中列出,它們每一個都有更詳細的說明的鏈接。
表 6-1? Bean 定義的解釋
特 ??? 性 |
說 ??? 明 |
class |
bean 的類 |
id 和 name |
bean 的標志符 (id 與 name) |
singleton 或 prototype |
Singleton 的使用與否 |
構(gòu)造函數(shù)參數(shù) |
設(shè)置 bean 的屬性和合作者 |
bean 的屬性 |
設(shè)置 bean 的屬性和合作者 |
自動裝配模式 |
自動裝配協(xié)作對象 |
依賴檢查模式 |
依賴檢查 |
初始化模式 |
生命周期接口 |
析構(gòu)方法 |
生命周期接口 |
注 意, bean 定義可以表示為真正的接口 org.springframework.beans.factory.config.BeanDefinition 以及它的各種子接口和實現(xiàn)。然而,絕大多數(shù)的用戶代碼不需要與 BeanDefination 直接接觸。
3 . bean 類
class 屬性通常是強制性的,有兩種用法。在絕大多數(shù)情況下, BeanFactory 直接調(diào)用 bean 的構(gòu)造函數(shù)來“ new ”一個 bean (相當于調(diào)用 new 的 Java 代碼), class 屬性指定了需要創(chuàng)建的 bean 的類。在比較少的情況下, BeanFactory 調(diào)用某個類的靜態(tài)的工廠方法來創(chuàng)建 bean , class 屬性指定了實際包含靜態(tài)工廠方法的那個類(至于靜態(tài)工廠方法返回的 bean 的類型是同一個類還是完全不同的另一個類,這并不重要)。
1 )通過構(gòu)造函數(shù)創(chuàng)建 bean
當使用構(gòu)造函數(shù)創(chuàng)建 bean 時,所有普通的類都可以被 Spring 使用,并且和 Spring 兼容。這就是說,被創(chuàng)建的類不需要實現(xiàn)任何特定的接口或者按照特定的樣式進行編寫。僅僅指定 bean 的類就足夠了。然而,根據(jù) bean 使用的 IoC 類型,你可能需要一個默認的(空的)構(gòu)造函數(shù)。
另外, BeanFactory 并不局限于管理真正的 JavaBean ,它也能管理任何你想讓它管理的類。雖然很多使用 Spring 的人喜歡在 BeanFactory 中用真正的 JavaBean (僅包含一個默認的(無參數(shù)的)構(gòu)造函數(shù),在屬性后面定義相對應(yīng)的 setter 和 getter 方法),但是在你的 BeanFactory 中也可以使用特殊的非 bean 樣式的類。舉例來說,如果你需要使用一個遺留下來的完全沒有遵守 JavaBean 規(guī)范的連接池,不要擔(dān)心, Spring 同樣能夠管理它。
使用 XmlBeanFactory 你可以像下面這樣定義你的 bean class 。
<bean id="exampleBean"
class="examples.ExampleBean"/>
<bean name="anotherExample"
class="examples.ExampleBeanTwo"/>
至于為構(gòu)造函數(shù)提供(可選的)參數(shù),以及對象實例創(chuàng)建后設(shè)置實例屬性,將會在后面敘述。
2 )通過靜態(tài)工廠方法創(chuàng)建 bean
當你定義一個使用靜態(tài)工廠方法創(chuàng)建的 bean ,同時使用 class 屬性指定包含靜態(tài)工廠方法的類,這個時候需要 factory-method 屬性來指定工廠方法名。 Spring 調(diào)用這個方法(包含一組可選的參數(shù))并返回一個有效的對象,之后這個對象就完全和構(gòu)造方法創(chuàng)建的對象一樣。用戶可以使用這樣的 bean 定義在遺留代碼中調(diào)用靜態(tài)工廠。
下面是一個 bean 定義的例子,聲明這個 bean 要通過 factory-method 指定的方法創(chuàng)建。注意,這個 bean 定義并沒有指定返回對象的類型,只指定包含工廠方法的類。在這個例子中, createInstance 必須是 static 方法。
<bean id="exampleBean" ?class="examples.ExampleBean2" ?factory-method="createInstance"/>
至于為工廠方法提供(可選的)參數(shù),以及對象實例被工廠方法創(chuàng)建后設(shè)置實例屬性,將會在后面敘述。
3 )通過實例工廠方法創(chuàng)建 bean
使用一個實例工廠方法(非靜態(tài)的)創(chuàng)建 bean 和使用靜態(tài)工廠方法非常類似,調(diào)用一個已存在的 bean (這個 bean 應(yīng)該是工廠類型)的工廠方法來創(chuàng)建新的 bean 。
使用這種機制, class 屬性必須為空,而且 factory-bean 屬性必須指定一個 bean 的名字,這個 bean 一定要在當前的 bean 工廠或者父 bean 工廠中,并包含工廠方法。而工廠方法本身仍然要通過 factory-method 屬性設(shè)置。
下面是一個例子。
<!-- The factory bean, which contains a method called??createInstance -->
<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 的屬性,但是,這個方法意味著工廠 bean 本身能夠被容器通過依賴注射來管理和配置。
4 . Bean 的標志符( id 與 name )
每一個 bean 都有一個或多個 id (也叫做標志符或名字,這些名詞說的是一回事)。這些 id 在管理 bean 的 BeanFactory 或 ApplicationContext 中必須是惟一的。一個 bean 差不多總是只有一個 id ,但是,如果一個 bean 有超過一個的 id ,那么另外的那些本質(zhì)上可以認為是別名。
在一個 XmlBeanFactory 中(包括 ApplicationContext 的形式),你可以用 id 或者 name 屬性來指定 bean 的 id(s) ,并且在這兩個或其中一個屬性中至少指定一個 id 。 id 屬性允許你指定一個 id ,并且它在 XML DTD (定義文檔)中作為一個真正的 XML 元素的 ID 屬性被標記,所以 XML 解析器能夠在其他元素指回向它的時候做一些額外的校驗。正因如此,用 id 屬性指定 bean 的 id 是一個比較好的方式。然而, XML 規(guī)范嚴格限定了在 XML ID 中合法的字符。通常這并不是真正限制你,但是,如果你有必要使用這些字符(在 ID 中的非法字符),或者你想給 bean 增加其他的別名,那么你可以通過 name 屬性指定一個或多個 id (用逗號或分號分隔)。
5 . Singleton 的使用與否
Beans 被定義為兩種部署模式中的一種: singleton 或 non-singleton (后一種也叫做 prototype ,盡管這個名詞用的不精確)。如果一個 bean 是 singleton 形態(tài)的,那么就只有一個共享的實例存在,所有和這個 bean 定義的 id 符合的 bean 請求都會返回這個惟一的、特定的實例。
如果 bean 以 non-singleton 、 prototype 模式部署的話,對這個 bean 的每次請求都會創(chuàng)建一個新的 bean 實例。這對于每個 user 需要一個獨立的 user 對象的情況是非常理想的。
Beans 默認被部署為 singleton 模式,除非你指定。要記住把部署模式變?yōu)?/span> non-singletion ( prototype )后,每一次對這個 bean 的請求都會導(dǎo)致一個新創(chuàng)建的 bean ,而這可能并不是你真正想要的。所以,僅僅在絕對需要的時候才把模式改成 prototype 。
在下面這個例子中,兩個 bean 一個被定義為 singleton ,而另一個被定義為 non-singleton ( prototype )。客戶端每次向 BeanFactory 請求都會創(chuàng)建新的 exampleBean ,而 AnotherExample 僅僅被創(chuàng)建一次,在每次對它請求都會返回這個實例的引用。
<bean id="exampleBean"
?class="examples.ExampleBean" singleton="false"/>
<bean name="yetAnotherExample"
?class="examples.ExampleBeanTwo" singleton="true"/>
注意,當部署一個
bean
為
prototype
模式,這個
bean
的生命周期就會有稍許改變。
通過定義,
Spring
無法管理一個
non-singleton/prototype bean
的整個生命周期,因為當它創(chuàng)建之后,它被交給客戶端,而且容器根本不再跟蹤它了。當說起
non-singleton/prototype bean
的時候,你可以把
Spring
的角色想像成“
new
”操作符的替代品。從那之后的任何生命周期方面的事情都由客戶端來處理。
二.Spring IOC反轉(zhuǎn)控制 ApplicationContext
beans 包提供了以編程的方式管理和操控 bean 的基本功能,而 context 包增加了 ApplicationContext ,它以一種更加面向框架的方式增強了 BeanFactory 的功能。多數(shù)用戶可以以一種完全的聲明式方式來使用 ApplicationContext ,甚至不用去手工創(chuàng)建它,但是卻去依賴像 ContextLoader 的支持類,在 J2EE 的 Web 應(yīng)用的啟動進程中用它啟動 ApplicationContext 。當然,這種情況下還可以以編程的方式創(chuàng)建一個 ApplicationContext 。
Context 包的基礎(chǔ)是位于 org.springframework.context 包中的 ApplicationContext 接口。它是由 BeanFactory 接口集成而來,提供 BeanFactory 所有的功能。為了以一種更像面向框架的方式工作, context 包使用分層和有繼承關(guān)系的上下文類,包括:
1
.
MessageSource
,提供對
i18n
消息的訪問;
2
.資源訪問,比如
URL
和文件;
3
.事件傳遞給實現(xiàn)了
ApplicationListener
接口的
bean
;
4
.載入多個(有繼承關(guān)系)上下文類,使得每一個上下文類都專注于一個特定的層次,比如應(yīng)用的
Web
層。
因為 ApplicationContext 包括了 BeanFactory 所有的功能,所以通常建議先于 BeanFactory 使用,除了有限的一些場合,比如在一個 Applet 中,內(nèi)存的消耗是關(guān)鍵的,每千字節(jié)都很重要。接下來,敘述 ApplicationContext 在 BeanFactory 的基本能力上增加的功能。
( 1 )使用 MessageSource
ApplicationContext 接口繼承 MessageSource 接口,所以提供了 messaging 功能( i18n 或者國際化)。同 NestingMessageSource 一起使用,就能夠處理分級的信息,這些是 Spring 提供的處理信息的基本接口。讓我們很快瀏覽一下這里定義的方法。
String getMessage (String code 、 Object[] args 、 String default 、 Locale loc) :這個方法是從 MessageSource 取得信息的基本方法。如果對于指定的 locale 沒有找到信息,則使用默認的信息。傳入的參數(shù) args 被用來代替信息中的占位符,這個是通過 Java 標準類庫的 MessageFormat 實現(xiàn)的。
String getMessage (String code 、 Object[] args 、 Locale loc) :本質(zhì)上和上一個方法是一樣的,除了一點區(qū)別:沒有默認值可以指定;如果信息找不到,就會拋出一個 NoSuchMessage Exception 。
String getMessage(MessageSourceResolvable resolvable 、 Locale locale) :上面兩個方法使用的所有屬性都可以封裝到一個叫做 MessageSourceResolvable 的類中,你可以通過這個方法直接使用它。
當 ApplicationContext 被加載的時候,它會自動查找在 context 中定義的 MessageSource bean ,這個 bean 必須叫做 message source 。如果找到了這樣的一個 bean ,所有對上述方法的調(diào)用將會被委托給找到的 message source 。如果沒有找到 message source , ApplicationContext 將會嘗試查它的父親是否包含這個名字的 bean 。如果有,它將會把找到的 bean 作為 Message Source 。如果它最終沒有找到任何信息源,一個空的 StaticMessageSource 將會被實例化,使它能夠接受上述方法的調(diào)用。
Spring 目前提供了兩個 MessageSource 的實現(xiàn),它們是 ResourceBundleMessageSource 和 StaticMessageSource 。它們都實現(xiàn)了 NestingMessageSource , 以便能夠嵌套地解析信息。 StaticMessageSource 很少被使用,但是它提供以編程的方式向 source 增加信息。 Resource BundleMessageSource 用得更多一些,我們將提供它的一個例子。
<beans>?
? <bean id="messageSource"
?????????????? class="org.springframework.context.support.ResourceBundle MessageSource">
??????? <property name="basenames">
??????????? <list>
?????????????? <value>format</value>
??????????????? <value>exceptions</value>
??????????????? <value>windows</value>
??????????? </list>
??????? </property>
??? </bean>
</beans>
這段配置假定你在 classpath 有 3 個 resource bundle ,分別叫做 f format 、 exceptions 和 windows 。 使用 JDK 通過 ResourceBundle 解析信息的標準方式,任何解析信息的請求都會被處理。
( 2 )事件傳遞
ApplicationContext 中的事件處理是通過 ApplicationEvent 類和 ApplicationListener 接口來提供的。如果上下文中部署了一個實現(xiàn)了 ApplicationListener 接口的 bean ,每次一個 ApplicationEvent 發(fā)布到 ApplicationContext 時,那個 bean 就會被通知。實質(zhì)上,這是標準的 Observer 設(shè)計模式。 Spring 提供了 3 個標準事件,如表 6-2 所示。
表 6-2? 內(nèi)置事件
事 ??? 件 |
解 ??? 釋 |
ContextRefreshedEvent |
當 ApplicationContext 已經(jīng)初始化或刷新后發(fā)送的事件。這里初始化意味著所有的 bean 被裝載, singleton 被預(yù)實例化,以及 ApplicationContext 已準備好 |
ContextClosedEvent |
當使用 ApplicationContext 的 close() 方法結(jié)束上下文的時候發(fā)送的事件。這里結(jié)束意味著: singleton 被銷毀 ? |
RequestHandledEvent |
一個與 Web 相關(guān)的事件,告訴所有的 bean 一個 HTTP 請求已經(jīng)被響應(yīng)了(這個事件將會在一個請求結(jié)束后被發(fā)送)。注意,這個事件只能應(yīng)用于使用了 Spring 的 DispatcherServlet 的 Web 應(yīng)用 |
同樣也可以實現(xiàn)自定義的事件。通過調(diào)用 ApplicationContext 的 publishEvent() 方法,并且指定一個參數(shù),這個參數(shù)是你自定義的事件類的一個實例。我們來看一個例子,首先是 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>
然后是實際的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
??????? }
??? }
}
?
( 3 )在 Spring 中使用資源
很多應(yīng)用程序都需要訪問資源。 Spring 提供了一個清晰透明的方案,以一種協(xié)議無關(guān)的方式訪問資源。 ApplicationContext 接口包含一個方法( getResource(String) )負責(zé)這項工作。
Resource 類定義了幾個方法,這幾個方法被所有的 Resource 實現(xiàn)所共享,資源功能如表 6-3 所示。
表 6-3? 資源功能
方 ??? 法 |
解 ??? 釋 |
getInputStream() |
用 InputStream 打開資源,并返回這個 InputStream |
exists() |
檢查資源是否存在,如果不存在,返回 false |
isOpen() |
如果這個資源不能打開多個流,將會返回 true 。因為除了基于文件的資源,一些資源不能被同時多次讀取,它們就會返回 false |
getDescription() |
返回資源的描述,通常是全限定文件名或者實際的 URL |
Spring 提供了幾個 Resource 的實現(xiàn)。它們都需要一個 String 表示的資源的實際位置。依據(jù)這個 String , Spring 將會自動為你選擇正確的 Resource 實現(xiàn)。當向 ApplicationContext 請求一個資源時, Spring 首先檢查你指定的資源位置,尋找任何前綴。根據(jù)不同的 Application Context 的實現(xiàn),不同的 Resource 實現(xiàn)可被使用。 Resource 最好是使用 ResourceEditor 來配置,比如 XmlBeanFactory 。