如何解決客戶端對(duì)象依賴具體實(shí)現(xiàn)類的問題呢?
// MemoryUserDao是UserDao的具體實(shí)現(xiàn)
UserDao userDao = new MemoryUserDao();
userDao.save(user);
解決方案:工廠(Factory)模式
使用一個(gè)工廠類來實(shí)現(xiàn) userDao 對(duì)象的創(chuàng)建,這樣客戶端只要知道這一個(gè)工廠類就可以了,不用依賴任何具 體的 UserDao 實(shí)現(xiàn)
創(chuàng)建 userDao 對(duì)象的工廠類 UserDaoFactory 代碼如下:
public class UserDaoFactory {
public static UserDao createUserDao(){
return new MemoryUserDao();
}
}
客戶端 UserRegister 代碼片斷如下:
UserDao userDao = UserDaoFactory. CreateUserDao();
userDao.save(user);
現(xiàn)在如果再要更換持久化方式,比如使用文本文件持久化用戶信息。就算有再多的客戶代碼調(diào)用了用戶持久化對(duì)象我們都不用擔(dān)心了烦租。因?yàn)榭蛻舳撕陀脩舫志没瘜?duì)象的具體實(shí)現(xiàn)完全解耦嵌赠。我們唯一要修改的只是一個(gè)類橱夭。
工廠模式改進(jìn)
消除更換持久化方式時(shí)帶來的硬編碼。
在屬性文件中可以這樣配置: userDao = com.test.MemoryUserDao 。 UserDao 的工廠類將從這個(gè)屬性文件 中取得 UserDao 實(shí)現(xiàn)類的全名
public class UserDaoFactory {
public static UserDao createUserDao(){
String className = "";
// ......從屬性文件中取得這個(gè)UserDao的實(shí)現(xiàn)類全名。
UserDao userDao = null;
try {
userDao = (UserDao)Class.forName(className).newInstance();
} catch (Exception e) {
e.printStackTrace(); }
return userDao;
}
工廠模式的終極方案——IoC 模式
使用 IoC 容器
public class UserRegister {
private UserDao userDao = null;//由容器注入的實(shí)例對(duì)象
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
// UserRegister的業(yè)務(wù)方法
}
Spring 的配置文件 applicationContext.xml 代碼片斷如下:
<bean id="userRegister" class="com.dev.spring.simple.UserRegister">
<property name="userDao"><ref local="userDao"/></property>
</bean>
<bean id="userDao" class="com.dev.spring.simple.MemoryUserDao"/>
BeanFactory
BeanFactory 是 Spring 的“心臟”钝诚。它就是 Spring IoC 容器的真面目。Spring 使用 BeanFactory 來實(shí)例化择卦、配置和管理 Bean敲长。但是,在大多數(shù)情況我們并不直接使用 BeanFactory,而是使用 ApplicationContext。它也是 BeanFactory 的一個(gè)實(shí)現(xiàn),但是它添加了一系列“框架”的特征,比如:國際化支持秉继、資源訪問祈噪、事件傳播等。
BeanFactory 其實(shí)是一個(gè)接口-org.springframework.beans.factory.BeanFactory,它可以配置和管理幾乎所有的 Java 類尚辑。當(dāng)然,具體的工作是由實(shí)現(xiàn) BeanFactory 接口的實(shí)現(xiàn)類完成辑鲤。我們最常用的 BeanFactory 實(shí)現(xiàn)是org.springframework.beans.factory.xml.XmlBeanFactory。它從 XML 文件中讀取 Bean 的定義信 息杠茬。當(dāng) BeanFactory 被創(chuàng)建時(shí),Spring 驗(yàn)證每個(gè) Bean 的配置月褥。當(dāng)然,要等 Bean 創(chuàng)建之后才能設(shè)置 Bean 的屬性弛随。單例(Singleton)Bean 在啟動(dòng)時(shí)就會(huì)被 BeanFactory 實(shí)例化,其它的 Bean 在請(qǐng)求時(shí)創(chuàng)建。根據(jù) BeanFactory 的 Java 文檔(Javadocs)介紹,“Bean 定義的持久化方式?jīng)]有任何的限制:LDAP宁赤、RDBM S舀透、XML、屬性文件,等等”【鲎螅現(xiàn)在 Spring 已提供了 XML 文件和屬性文件的實(shí)現(xiàn)愕够。無疑,XML 文件是定義 Bean 的最佳方式。
BeanFactory 是初始化 Bean 和調(diào)用它們生命周期方法的“吃苦耐勞者”佛猛。注意,BeanFactory 只能管理單 例(Singleton)Bean 的生命周期惑芭。它不能管理原型(prototype,非單例)Bean 的生命周期。這是因?yàn)樵?Bea n 實(shí)例被創(chuàng)建之后便被傳給了客戶端,容器失去了對(duì)它們的引用继找。
BeanFactory管理Bean(組件)的生命周期
Bean 的生命周期遂跟,它是由 IoC 容器控制。IoC 容器定義 Bean 操作的規(guī)則,即 Bean 的定義婴渡。Bean 的定義包含了 BeanFactory 在創(chuàng)建 Bean 實(shí)例時(shí)需要的所有信息幻锁。BeanFactory 首先 通過構(gòu)造函數(shù)創(chuàng)建一個(gè) Bean 實(shí)例,之后它會(huì)執(zhí)行 Bean 實(shí)例的一系列之前初始化動(dòng)作,初始化結(jié)束 Bean 將進(jìn) 入準(zhǔn)備就緒(ready)狀態(tài),這時(shí)應(yīng)用程序就可以獲取這些 Bean 實(shí)例了。最后,當(dāng)你銷毀單例(Singleton)B ean 時(shí),它會(huì)調(diào)用相應(yīng)的銷毀方法,結(jié)束 Bean 實(shí)例的生命周期边臼。
使用 Spring 定義了一個(gè)用戶持久化類:
<bean id="userDao" class="com.dev.spring.simple.MemoryUserDao"/>
這是一個(gè)最簡單的 Bean 定義越败。它類似于調(diào)用了語句:
MemoryUserDao userDao = new MemoryUserDao()。
id屬性必須是一個(gè)有效的 XML ID,這意味著它在整個(gè) XML 文檔中必須唯一硼瓣。它是一個(gè) Bean 的“終身代號(hào)(95 27)”。同時(shí)你也可以用 name 屬性為 Bean 定義一個(gè)或多個(gè)別名(用逗號(hào)或空格分開多個(gè)別名)置谦。name 屬 性允許出現(xiàn)任意非法的 XML 字母堂鲤。
name="userDao*_1, userDao*_2"
class屬性定義了這個(gè) Bean 的類名(包名+類名)。Spring 能管理幾乎所有的 Java 類媒峡。一般情況,這 個(gè) Java 類會(huì)有一個(gè)默認(rèn)的構(gòu)造函數(shù),用 set 方法設(shè)置依賴的屬性瘟栖。
Bean 屬性:
<bean
# id:Bean 的唯一標(biāo)識(shí)名。它必須是合法的 XML ID,在整個(gè) XML 文檔中唯一谅阿。
id="beanId"(1)
# name: 用來為 id 創(chuàng)建一個(gè)或多個(gè)別名半哟。它可以是任意的字母符合。多個(gè)別名之間用逗號(hào)或空格分開签餐。
name="beanName"(2)
# class: 用來定義類的全限定名(包名+類名)寓涨。只有子類 Bean 不用定義該屬性。
class="beanClass"(3)
# parent: 子類 Bean 定義它所引用它的父類 Bean氯檐。這時(shí)前面的 class 屬性失效戒良。子類 Bean 會(huì)繼承父類 Bean 的所有屬性,子類 Bean 也可以覆蓋父類 Bean 的屬性。注意:子類 Bean 和父類 Bean 是同一個(gè) Java 類冠摄。
parent="parentBean"(4)
# abstract(默認(rèn)為”false”):用來定義 Bean 是否為抽象 Bean糯崎。它表示這個(gè) Bean 將不會(huì)被實(shí)例 化,一般用于父類 Bean,因?yàn)楦割?Bean 主要是供子類 Bean 繼承使用几缭。
abstract="true | false"(5)
# singleton(默認(rèn)為“true”):定義 Bean 是否是 Singleton(單例)。如果設(shè)為“true”,則在 BeanF actory 作用范圍內(nèi),只維護(hù)此 Bean 的一個(gè)實(shí)例沃呢。如果設(shè)為“flase”,Bean將是 Prototype(原型)狀態(tài),B eanFactory 將為每次 Bean 請(qǐng)求創(chuàng)建一個(gè)新的 Bean 實(shí)例年栓。
singleton="true | false"(6)
# lazy-init(默認(rèn)為“default”):用來定義這個(gè) Bean 是否實(shí)現(xiàn)懶初始化。如果為“true”,它將在 Bea nFactory 啟動(dòng)時(shí)初始化所有的 Singleton Bean薄霜。反之,如果為“false”,它只在 Bean 請(qǐng)求時(shí)才開始創(chuàng)建 Sing leton Bean某抓。
lazy-init="true | false | default"(7)
# autowire(自動(dòng)裝配,默認(rèn)為“default”):它定義了 Bean 的自動(dòng)裝載方式。
? “no”:不使用自動(dòng)裝配功能黄锤。
? “byName”:通過 Bean 的屬性名實(shí)現(xiàn)自動(dòng)裝配搪缨。
? “byType”:通過 Bean 的類型實(shí)現(xiàn)自動(dòng)裝配。
? “constructor”:類似于 byType,但它是用于構(gòu)造函數(shù)的參數(shù)的自動(dòng)組裝鸵熟。
? “autodetect”:通過 Bean 類的反省機(jī)制(introspection)決定是使用“constructor”還是使用“byT ype”副编。
autowire="no | byName | byType | constructor | autodetect | default"(8)
# dependency-check(依賴檢查,默認(rèn)為“default”):它用來確保 Bean 組件通過 JavaBean 描述的 所以依賴關(guān)系都得到滿足。在與自動(dòng)裝配功能一起使用時(shí),它特別有用流强。
? none:不進(jìn)行依賴檢查痹届。
? objects:只做對(duì)象間依賴的檢查。
? simple:只做原始類型和 String 類型依賴的檢查
? all:對(duì)所有類型的依賴進(jìn)行檢查队腐。它包括了前面的 objects 和 simple。dependency-check = "none | objects | simple | all | default"(9)
# depends-on(依賴對(duì)象):這個(gè) Bean 在初始化時(shí)依賴的對(duì)象,這個(gè)對(duì)象會(huì)在這個(gè) Bean 初始化之前 創(chuàng)建奏篙。
depends-on="dependsOnBean"(10)
# init-method:用來定義 Bean 的初始化方法,它會(huì)在 Bean 組裝之后調(diào)用柴淘。它必須是一個(gè)無參數(shù)的方法。
init-method="method"(11)
# destroy-method:用來定義 Bean 的銷毀方法,它在 BeanFactory 關(guān)閉時(shí)調(diào)用秘通。同樣,它也必須是一 個(gè)無參數(shù)的方法为严。它只能應(yīng)用于singleton Bean。
destroy-method="method"(12)
# factory-method:定義創(chuàng)建該 Bean 對(duì)象的工廠方法肺稀。它用于下面的“factory-bean”,表示這個(gè) Be an 是通過工廠方法創(chuàng)建第股。此時(shí),“class”屬性失效。
factory-method="method"(13)
# factory-bean:定義創(chuàng)建該 Bean 對(duì)象的工廠類话原。如果使用了“factory-bean”則“class”屬性失效夕吻。
factory-bean="bean">(14)
</bean>
根據(jù) bean 屬性中描述的對(duì)象依賴來組裝(wire)bean 實(shí)例。如:userDao 對(duì)象 的一個(gè)屬性“sessionFactory”引用了另外一個(gè) Bean 對(duì)象:
<bean id="userDao" class="com.dev.spring.simple.HibernateUserDao">
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean"> </bean>
在 ref 標(biāo)簽中,我們使用了一個(gè)“l(fā)ocal”屬性指定它所引用的Bean對(duì)象繁仁。除了 local 屬性之外,還有一些其它的屬性可以用來指定引用對(duì)象涉馅。下面列出元素的所有可用的指定方式:
- bean:可以在當(dāng)前文件中查找依賴對(duì)象,也可以在應(yīng)用上下文(ApplicationContext)中查找其它配置文件的對(duì)象。
- local:只在當(dāng)前文件中查找依賴對(duì)象黄虱。這個(gè)屬性是一個(gè) XML IDREF,所以它指定的對(duì)象必須存在,否則它的驗(yàn)證檢查會(huì)報(bào)錯(cuò)控漠。
- external:在其它文件中查找依賴對(duì)象,而不在當(dāng)前文件中查找。
總的來說,和大部分的時(shí)候可以通用⊙谓荩“bean”是最靈活的方式,它允許你在多個(gè)文件之間共享 Bean偶翅。而“l(fā)o cal”則提供了便利的XML驗(yàn)證。
復(fù)雜的屬性值
Spring 的 bean 工廠不僅允許用 String 值和其他 bean 的引用作為 bean 組件的屬性值,還支持更復(fù)雜的值,例如數(shù)組碉渡、java.util.List聚谁、java.util.Map和java.util.Properties。數(shù)組滞诺、set形导、list和map中的值不僅可以是 String 類型,也可以是其他 bean 的引用;map 中的鍵、Properties 的鍵和值都必須是 String 類型的;map 中的值可以是 set习霹、list 或者 map 類型 朵耕。
Null:
<property name=“bar”><null/></property>
List和數(shù)組:
<property name=“bar”>
<list>
<value>ABC</value>
<value>123</value>
</list>
</property>
Map:
<property name=“bar”>
<map>
<entry key=“key1”><value>ABC</value></entry>
<entry key=“key2”><value>123</value></entry>
</set>
</property>
Bean初始化
Bean 工廠使用 Bean 的構(gòu)造函數(shù)創(chuàng)建 Bean 對(duì)象之后,緊接著它會(huì)做一件非常重要的工作——Bean 的初始 化。它會(huì)根據(jù)配置信息設(shè)置 Bean 的屬性和依賴對(duì)象,執(zhí)行相應(yīng)的初始化方法淋叶。
自動(dòng)裝配
一般不推薦在大型的應(yīng)用系統(tǒng)中使用自動(dòng)裝配阎曹。當(dāng)然,它可以很好的用于小型應(yīng)用系統(tǒng)。如果一個(gè) Bean 聲明被標(biāo)志為“autowire(自動(dòng)裝配)”,bean 工廠會(huì)自動(dòng)將其他的受管對(duì)象與其要求的依賴關(guān)系進(jìn)行匹配,從而完成對(duì)象的裝配——當(dāng)然,只有當(dāng)對(duì)象關(guān)系無歧義時(shí)才能完成自動(dòng)裝配煞檩。因?yàn)椴恍枰鞔_指定某個(gè)協(xié)作對(duì)象,所以 可以帶來很多的便利性处嫌。
舉個(gè)例子,如果在 Bean 工廠中有一個(gè) SessionFactory 類型的實(shí)例,HibernateUserDao 的 屬性就可以獲得這個(gè)實(shí)例。這里可以使用的 autowire 屬性,就象這樣:
<bean id="userDao" class="com.dev.spring.simple.HibernateUserDao" autowire=”byType”>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
.....
</bean>
注意,在 userDao 的定義中并沒有明確引用斟湃。由于它的“autowire”被設(shè)置為“byType”,所有只要有一個(gè)類型為的屬性并有一個(gè) set 方法,Bean 工廠就會(huì)自動(dòng)將組裝到中熏迹。
如果一個(gè)應(yīng)用有兩個(gè)數(shù)據(jù)庫,這時(shí)就對(duì)應(yīng)有兩個(gè)。這時(shí) autowire="byType"就無法使用了凝赛。我們可以使用另外一種自動(dòng)裝配方式“byName”注暗。它將根據(jù)屬性的名稱來匹配依賴對(duì)象,這樣如果你的配置文件中可以同時(shí)存在多個(gè)類型相同的,只要他們定義的名稱不同就可以了。這種方式的缺點(diǎn)是:需要精確匹配 Bean 的名稱,即必須要保證屬性的名稱和它所依賴的 Bean 的名稱相等,這樣比較容易出錯(cuò)墓猎。
注意:我們還是強(qiáng)烈推薦手工指定 Bean 之間的依賴關(guān)系友存。這種用法最強(qiáng)大,因?yàn)樗试S按名稱引用特定的 Be an 實(shí)例,即使多個(gè) Bean 具有相同類型也不會(huì)混淆。同時(shí),你可以清楚的知道一個(gè) Bean 到底依賴哪些其它的 Bean陶衅。如果使用自動(dòng)裝載,你只能去 Bean 的代碼中了解。甚至,Bean 工廠也許會(huì)自動(dòng)裝載一些你根本不想 依賴的對(duì)象直晨。
依賴檢查
如果你希望 Bean 嚴(yán)格的設(shè)置所有的屬性,“dependency-check”(依賴檢查)屬性將會(huì)非常有用搀军。它默認(rèn)為“none”,不進(jìn)行依賴檢查∮禄剩“simple”會(huì)核對(duì)所有的原始類型和 String 類型的屬性罩句。“objects”只做對(duì)象間的關(guān)聯(lián)檢查(包括集合)敛摘∶爬茫“all”會(huì)檢查所有的屬性,包括“simple”和“objects”。
舉個(gè)例子:一個(gè) Bean 有如下的一個(gè)屬性:
private int intVar = 0;
public void setIntVar(int intVar) {
this.intVar = intVar;
}
這個(gè) Bean 的配置文件設(shè)置了“dependency-check=”simple””。如果這個(gè)Bean的配置中沒有定義這個(gè)屬性“intVar”,則在進(jìn)行這個(gè) Bean 的依賴檢查時(shí)就會(huì)拋出異常:org.springframework.beans.factory.Unsat isfiedDependencyException屯远。
setXXX()
set 方法非常簡單,它會(huì)給 class 注入所有依賴的屬性蔓姚。這些屬性都必須是在配置文件中使用元素定義,它們可以是原始類型,對(duì)象類型(Integer,Long),null 值,集合,其它對(duì)象的引用。
afterPropertiesSet()
有兩種方法可以實(shí)現(xiàn) Bean 的之前初始化方法慨丐。
- 使用“init-method”屬性,在 Spring 的配置文件中定義回調(diào)方法坡脐。
- 實(shí)現(xiàn)接口 InitializingBean 并實(shí)現(xiàn)它的 afterPropertiesSet() 方法。接口 InitializingBean 的代碼如下:
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
在 JavaBean 的所有屬性設(shè)置完成以后,容器會(huì)調(diào)用 afterPropertiesSet() 方法,應(yīng)用對(duì)象可以在這里執(zhí)行任何定制的初始化操作房揭。這個(gè)方法允許拋出最基本的 Exception 異常,這樣可以簡化編程模型备闲。
在 Spring 框架內(nèi)部,很多 bean 組件都實(shí)現(xiàn)了這些回調(diào)接口。但我們的 Bean 組件最好不要通過這種方式實(shí)現(xiàn) 生命周期的回調(diào),因?yàn)樗蕾囉?Spring 的 API捅暴。無疑,第一種方法是我們的最佳選擇恬砂。
init-method
init-method 的功能和 InitializingBean 接口一樣。它定義了一個(gè) Bean 的初始化方法,在 Bean 的所有屬性設(shè) 置完成之后自動(dòng)調(diào)用蓬痒。這個(gè)初始化方法不用依賴于 Spring 的任何 API泻骤。它必須是一個(gè)無參數(shù)的方法,可以拋出 Exception。
例如:我們的 Bean 組件 UserManger 中定義一個(gè)初始化方法 init() 乳幸。這樣,我們就可以在 Bean 定義時(shí)指定這 個(gè)初始化方法:
<bean id=”userManger” class=”com.dev.spring.um.DefaultUserManager” init-method=”init”>
......
</bean>
Bean 的準(zhǔn)備就緒(Ready)狀態(tài)
Bean 完成所有的之前初始化之后,就進(jìn)入了準(zhǔn)備就緒(Ready)狀態(tài)瞪讼。這就意味著你的應(yīng)用程序可以取得這些 Bean,并根據(jù)需要使用他們。
在你關(guān)閉(或重啟)應(yīng)用程序時(shí),單例(Singleton)Bean 可以再次獲得生命周期的回調(diào),你可以在這時(shí)銷毀 Bean 的一些資源粹断。第一種方法是實(shí)現(xiàn) DisposableBean 接口并實(shí)現(xiàn)它的 destroy() 方法符欠。更好的方法是用“dest roy-method”在 Bean 的定義時(shí)指定銷毀方法。