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