在本章中作者講解了Spring的IoC概念,詳細介紹了如何配置我們自己的應用系統,如何編寫松耦合的組件。如何在XML中配置Bena。
這里提個建議,現在的Spring已經升級到2.0版本了,而本書(《Spring in Action》)出版時還是Spring1.X。2.0有了較大的變化建議新學Spring的人下載2.0版本的來學習,現在最新的版本已經是Rc3了。同時2.0版本的中文參考已經在Spring中文論壇有人組織翻譯了。而且現在已經有預覽版了,大家可以先參考一下。(Spring中文預覽版:http://www.jactiongroup.net/reference2/html/)同時向無償翻譯Spring文檔的這些高手們致敬。
一、容納你的Bean
容器是Spring的核心。Spring使用IoC管理所有組成應用系統的組件。Spring有兩種不同的容器:
l Bena工廠(BeanFactory):由org.springframework.beans.factory.BeanFactory接口定義。是最簡單的容器,提供了基礎的依賴注入支持。
l 應用上下文(ApplicationContext):由org.springframework.context.ApplicationContext接口定義。建立在Bean工廠之上提供了系統架構服務。
1.BeanFactory介紹
見名知意,BeanFactory采用了工廠模式(抽象工廠模式:http://blog.csdn.net/qutr/archive/2006/01/22/586034.aspx;工廠方法模式:http://blog.csdn.net/qutr/archive/2006/07/21/954070.aspx)。這個類專門負責創建類(對象)。有些書上說Spring的BeanFactory像一個超負荷運轉的機器,因為除了簡單的創建對象以外,BeanFactory可以在實例化這些對象的時候,創建協作對象間的關聯關系。這樣就把配置的負擔從Bean自身以及Bean的調用者中脫離出來。更詳細的可以查看BeanFactory的源代碼。
在Spring中有幾種BeanFactory的實現。其中最長用的是org.springframework.beans.factory.xml.XmlBeanFactory,它根據XML文件中的定義裝載Bean。在XmlBeanFactory類中提供了兩個構造函數,通常我們用的是XmlBeanFactory(Resource resource) throws BeansException這個。他一般這樣定義:BeanFactory factory = new XmlBeanFactory(new FileSystemResource(“beans.xml”));其中傳遞的參數引用為:import org.springframework.core.io.FileSystemResource;(書中這里是不太正確的,因為在Spring2.0里已經不提供傳遞java.io.InputStream對象的構造函數了。翻譯者非常的不負責任,應該在這里給出說明。)
從BeanFactory得到一個Bean只需調用getBean(“beanName”)方法,把你需要的Bean的名字當作參數傳遞進去就可以了。像這樣:MyBean myBean = (MyBean)factory.getBean(“myBean”);當getBean()方法被調用的時候,工廠就會開始實例化Bean 并且使用依賴注入開始設置Bean的屬性。
事實上BeanFactory接口提供有6種方法供客戶代碼調用:
(1)boolean containsBean(String): 如果BeanFactory包含符合給定名稱的Bean定義或Bean實例,則返回true。
(2)Object getBean(String): 返回以給定名字注冊的Bean實例。就是上面提到的最簡單的一種。
(3)Object getBean(String, Class): 返回以給定名稱注冊的Bean,返回的Bean將被扔給(cast)給定的類。如果Bean不能被Cast,相應的異常(BeanNotOfRequiredTypeException)將被拋出。
(4)Class getType(String name): 返回給定名稱的Bean的Class。如果沒有相應于給定名稱的Bean,則拋出NoSuchBeanDefinitionException異常。
(5)boolean isSingleton(String): 決定在給定名稱時,Bean定義或Bean實例注冊是否為單件模式,如果相應于給定名稱的Bean不能被找到,則拋出NoSuchBeanDefinitionException異常。
(6)String[] getAliases(String): 如果在Bean的定義中有定義,則返回給定Bean名稱的別名。
2.使用應用上下文(ApplicationContext)
使用ApplicationContext可以獲得Sroing框架的強大功能。表面上ApplicationContext和BeanFactory差不多,但是ApplicationContext提供了更多的功能:
l ApplicationContext提供了文本信息解析工具,包括對國際化的支持。
l ApplicationContext提供了載入文件資源的通用方法,如載入圖片。
l ApplicationContext可以向注冊為監聽器的Bean發送事件。
l ApplicationContext在Spring2.0里可能還加了其他功能。
有三種ApplicationContext的實現經常被用到:
l ClassPathXmlApplicationContext—從類路徑中的XML文件載入上下文定義信息,把上下文定義文件當成類路徑資源。(可以在整個類路徑中尋找XML文件)
l FileSystemXmlApplicationContext—從文件系統中的XML文件載入上下文定義信息。(只能在指定的路徑中尋找XML文件)
l XmlWebApplicationContext—從Web系統中的XMLwenjian 載入上下文定義信息。
其中FileSystemXmlApplicationContext和ClassPathXmlApplicationContext(他們都包含在org.springframework.context.support包下)的使用形式分別如下:
ApplicationContext context = new FileSystemXmlApplicationContext(“c:/foo.xml”);
ApplicationContext context2 = new ClassPathXmlApplicationContext(“foo.xml”)
應用上下文會在上下文啟動后預載入所有的單實例Bean。確保當你需要的時候他們已經準備好了你的應用不需要等待他們被創建。
總之實例化一個Bean大概有三種方法:
第一種:
Resource resource = new FileSystemResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);
第二種:
ClassPathResource resource = new ClassPathResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);
第三種:
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"applicationContext.xml", "applicationContext-part2.xml"});
BeanFactory factory = (BeanFactory) context;
ApplicathionContext的超類是BeanFactory,所以理所當然的這里的context可以是BeanFactory。
3.Bean的生命周期
在書中作者用圖文并茂的方式解說了Bean在BeanFactory和ApplicationContext中的生命周期,從創建到銷毀的全過程。他們二者之間略有不同,書上講的比較詳細,這里就不羅列了。
二、基本裝配
在Spring容器內拼湊Bean叫做裝配。裝配Bean的時候,你是在告訴容器需要哪些Bean以及容器如何使用依賴注入將他們配合在一起。
1.使用XML裝配
Bean裝配在Spring中最常用的是XML文件。在前面提到過的XmlBeanFactory、ClassPathXmlApplicationContext、FileSystemXmlApplicationContext和XmlWebApplicationContext都支持用XML裝配Bean。
Spring規定了自己的XML文件格式,根元素為<Beans>,<Beans>有多個<Bean>子元素。每個<Bean>定義了一個Bean如何被裝載到Spring容器中。看下面的名為test.xml的XML文件:
<?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 = "foo"
class = "springinaction.foo">
</bean>
<bean id = "bar"
class = "springinaction.bar">
</bean>
</beans>
在Spring中,對一個Bean的最基本的配置包括Bean的ID和他的類全稱。在test.xml文件中其中<Beans>為根元素,bean id=”foo”標明一個Bean他的Id為foo,此處的foo是由程序員自己命名的,但是在一個大的系統中有好多個Bean這里要求XML中的Id要唯一,在程序代碼中如果想取得foo這個Bean就要用getBean(“foo”)了。class=”springinaction.foo”是Bean的全稱類名。
2.添加一個Bean
Spring中的Bean在缺省情況下是單實例模式(也叫單態模式,http://blog.csdn.net/qutr/archive/2006/02/28/611868.aspx)。如果想每次從容器中得到得到一個Bean的不同實例,需要將Bean定義為原型模式(http://blog.csdn.net/qutr/archive/2006/07/24/968297.aspx)。我們可以用Bean的singleton屬性來設置,缺省情況為true。
<bean id = "foo"
class = "springinaction.foo"
singleton="false">
在Bean的定義中用init-method來初始化某些參數,用destroy-method來銷毀對象。
<bean id = "foo"
class = "springinaction.foo"
singleton="false"
init-method="init"
destroy-method="destory">
Spring也提供了兩個接口來實現相同的功能:InitializingBean和DisposableBean。
3.通過Set方法注入依賴。
寫過JavaBean的人可能都熟悉get和set方法。Spring就是用Set注入來配置Bean的。<bean>的子元素<property>指明了使用它們的set方法來注入。你可以注入任何類型。
每個Bean(通常就是你定義的一個class)通常都會有一些簡單的類型成員,如int或String等等。通過使用<property>的子元素<value>可以設置基本類型的屬性(值)。
<bean id = "bar" class = "springinaction.bar">
<property name="barName">
<value>coffeeBar</value>
</property>
</bean>
如上所示,在我們的springinaction.bar類中定義了一個名為barName的String類型的成員,用<value>coffeeBar</value>中的coffeeBar來給他設置值。如:setName(“barName”);如果定義了一個int類型的成員我們可以在<value></value>中寫入一個數字。
利用property豐富的屬性我們可以為一個Bean引用其它Bean,通過<ref>實現;可以嵌入一個內部Bean,通常很少使用。可以裝配各種集合,如java.util.List, java.util.Set, java.util.Map等等。可以設置properties和null值。
下面給出一個例子,這個例子同樣來自官方文檔。
<bean id="moreComplexObject" class="example.ComplexObject">
<!-- java.util.Properties -->
<property name="adminEmails">
<props>
<prop key="administrator">administrator@somecompany.org</prop>
<prop key="support">support@somecompany.org</prop>
<prop key="development">development@somecompany.org</prop>
</props>
</property>
<!-- java.util.List -->
<property name="someList">
<list>
<value>a list element followed by a reference</value>
<ref bean="myDataSource" />
</list>
</property>
<!-- java.util.Map -->
<property name="someMap">
<map>
<entry>
<key>
<value>yup an entry</value>
</key>
<value>just some string</value>
</entry>
<entry>
<key>
<value>yup a ref</value>
</key>
<ref bean="myDataSource" />
</entry>
</map>
</property>
<!-- java.util.Set -->
<property name="someSet">
<set>
<value>just some string</value>
<ref bean="myDataSource" />
</set>
</property>
</bean>
一目了然,非常清楚就不多解釋了。關于集合Spring2.0又添加了新的內容,如:你如果使用的是JDK1.5那么還可以使用Java的泛型來清晰的解析各種容器所包含的類型,請參看:http://static.springframework.org/spring/docs/2.0.x/reference/beans.html#beans-collection-elements。
4.通過構造函數注入依賴
Set注入是Srping所推薦的,但是Set注入的缺點是,他無法清晰的表示出哪些屬性是必須的,哪些是可選的。而使用構造函數注入的優勢是通過構造函數來強制以來關系,有了構造函數的約束不可能實現一個不完全或無法使用的Bean。
<bean id="coo" class="springinaction.coo">
<constructor-arg>
<value>cool</value>
</constructor-arg>
</bean>
上面的例子通過構造函數傳值來實例化一個coo對象。如coo co = new coo(“cool”);
如果有多個構造函數,那么可以設置<constructor-arg>的type或index來傳入參數了如果類型都一樣那么只能用index了,index的值是從0開始的。
Spring為我們提供了Set注入和構造函數注入,在兩者的使用方式上個人有個人的理由和不同見解,在本書中作者給我們的建議大概是:看情況,那個合適用哪個(廢話J)。但是,Spring的開發團隊通常提倡人們使用setter注入。其實Spring一共給我提供了三種注入方式:接口注入和上面的兩種注入。他們都有各自的優點,詳細的說明見《Spring開發指南》。
下面再給出兩個例子來說明一下Setter注入和constructor注入:
<bean id="exampleBean"
class="examples.ExampleBean">
<!-- 用嵌套的 <ref/> 元素進行setter注入 -->
<property name="beanOne">
<ref bean="anotherExampleBean"/>
</property>
<!-- 用嵌套的 'ref' 屬性進行setter注入 (注意:如果你看得是翻譯的2.0的預覽版這里是錯誤的,請和官方的英文版對照看)-->
<property name="beanTwo" ref = "yetAnotherBean"/><!—我這里是正確的,2.0的中文預覽版這里寫錯了-->
<property name="integerProperty" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
Spring提供了快捷的ref屬性,來代替ref元素,但是使用ref屬性你要小!
下面是一個類
publicclass ExampleBean
{
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
privateinti;
publicvoid setBeanOne(AnotherBean beanOne) {
this.beanOne = beanOne;
}
publicvoid setBeanTwo(YetAnotherBean beanTwo) {
this.beanTwo = beanTwo;
}
publicvoid setIntegerProperty(int i) {
this.i = i;
}
}
上面是一個Bean(其實就是一個普通的不能在普通的Java類)和它相對應的XML的配置文件,是一個典型的setter注入。例子清晰易懂就不多說了。
<bean id="exampleBean"
class="examples.ExampleBean">
<!-- 用嵌套的 <ref/> 元素進行構造函數注入 -->
<constructor-arg>
<ref bean="anotherExampleBean"/>
</constructor-arg>
<!-- 用嵌套的 'ref' 屬性進行構造函數注入 -->
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg type="int" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
下面是一個類
publicclass ExampleBean
{
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
privateinti;
public ExampleBean(AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i)
{
this.beanOne = anotherBean;
this.beanTwo = yetAnotherBean;
this.i = i;
}
}
上面是一個Bean和它相對應的XML的配置文件,是一個典型的constructor注入。例子也比較清晰易懂這里也就不多說了。
三、自動裝配
在前面講到的都是手動顯示裝配Bean的方法,Spring還提供了自動裝配Bean的方法。只要設置<Bean>的autowire屬性。有四種自動裝配類型:
l byName:在容器中尋找和需要自動裝配的屬性名相同的Bean(或ID)。如果沒有找到這個屬性就沒有裝配上。
l byType:在容器中尋找一個與自動裝配的屬性類型相同的Bean。如果沒有找到這個屬性就沒有裝配上。如果找到超過一個則拋出org.springframework.beans.factory.UnsatisfiedDependencyException異常。
l constructor:在容器中查找與需要自動裝配的Bean的構造函數參數一致的一個或多個Bean。如果存在不確定Bean或構造函數容器會拋出org.springframework.beans.factory.UnsatisfiedDependencyException異常。
l autodetect:首先嘗試使用constuctor來自動裝配,然后使用byType方式。不確定性的處理與constructor方式和byType方式一樣,都拋出org.springframework.beans.factory.UnsatisfiedDependencyException異常。
如下實例:
在前面的顯示裝配:
<bean id="courseService"
class="springinaction.chapter02.CourseServiceImpl">
<property name="courseDao">
<ref bean="courseDao"/>
</property>
<property name="studentService">
<ref bean="studentService"/>
</property>
</bean>
下面是自動裝配:
<bean id="courseService"
class="springinaction.chapter02.CourseServiceImpl"
autowire="byName"/>
在Spring對Bean自動裝配的過程中很容易出現不確定性,這些不確定性會導致程序無法運行。那么在我們的實際應用中要避免出現裝配的不確定性。避免裝配的不確定性的最好的方法就是使用顯示裝配和自動裝配的混合方法。對有二義性的Bena使用顯示裝配,對沒有二義性的Bean使用自動裝配。
在通常情況下我們會分門別類的吧Bena設置在多個XML文件中。另外一種方法是使用一個或多個的<import/>(就像我們寫Java程序要引入必要的包下的類一樣)元素來從另外一個或多個文件加載Bean定義。需要注意的是:任何<import/>元素必須放在配置文件中的<bean/>元素之前以完成Bean定義的導入。如下示例:
<beans>
<import resource="services.xml"/>
<import resource="resources/messageSource.xml"/>
<import resource="/resources/themeSource.xml"/>
<bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
<beans/>
上例中,三行import引入了必要的XML文件。兩行bean定義了Bean。依照XML的Schema或DTD,被導入文件必須是完全有效的XMLBean定義文件,且包含上層的<beans/>元素。
四、使用Spring的特殊Bean和應用舉例
在書中談到的Spring的特殊Bean的使用方法,我個人覺得這時Spring的IoC的高級用法或者是邊角用法,由于我也是新手初學這里就不多啰嗦了,等以后在實踐中有了體會在補上,關于這方面的內容在Spring2.0的官方文檔上都有提到,這份文檔非常的詳細,我個人覺得只要把這份文檔好好看了其他的書甚至可以放在一邊了,當然Rod寫的書還是要好好看的。
關于應用舉例,本書中在這章舉的例子不是很全面,剛開了個頭后面感覺就不說了。我本來想將我最近正做的一個實際應用中的例子放到這里,但是我的這個程序好像有點為了Spring而Spring所以讓我改了又改,至今也沒有出來所以也就不再這里顯示了。等以后專門寫一篇文章來討論一下。
五、小結
Spring框架的核心是Spring容器。BeanFactory是最簡單的容器,他提供了最基本的依賴注入和Bean裝配服務。但是,畢竟Spring是一個輕量級框架,要想得到更多的高級框架服務時我們需要使用ApplicationContext容器,所以為了讓你的應用程序看起來更高級更上檔次盡量多的使用ApplicationContext,這也是本書作者所提倡的。
其實關于Spring的IoC的核心和大部分內容在上面的文字中已經提到了,說來說去表面上就那些東西了。而IoC(DI)的真正思想是要靠大量的實踐去體會和領悟的。
上面是我對書中知識點的羅列和自己的一點理解(約占1%100J)請大家指教!
posted on 2007-10-23 21:43
譚明 閱讀(860)
評論(0) 編輯 收藏 所屬分類:
Spring