|
序言 軟件開(kāi)發(fā)者還必須具有好奇心。軟件業(yè)中的開(kāi)發(fā)總是不斷更換目標(biāo),不斷進(jìn)化。新框架,新技術(shù),新語(yǔ)言和新方法學(xué)總是不斷地出現(xiàn)。每一樣都是新工具,需要我們掌握和加入到我們的工具箱中,使我們能夠更好更快地完成我們的工作。 最后一個(gè)特征,也是最受珍愛(ài)的特征就是“惰性”。這種惰性刺激開(kāi)發(fā)者努力工作來(lái)尋找需要最少工作量的解決方案。正是有了這種好奇心,惰性和我們擁有的所有的分析能力,四年前我們倆開(kāi)始行動(dòng)起來(lái),尋找開(kāi)發(fā)軟件的新方法。 當(dāng)今時(shí)代,Java社區(qū)的開(kāi)源軟件已經(jīng)達(dá)到了一個(gè)關(guān)鍵的數(shù)量。無(wú)數(shù)的開(kāi)源框架在Java世界遍地開(kāi)花。為決定選用一種框架,它必需能極大滿(mǎn)足我們的需要—它必需能滿(mǎn)足我們80%的需求。對(duì)于任何不能滿(mǎn)足我們的功能,框架必需容易地?cái)U(kuò)展來(lái)包含這些功能。擴(kuò)展并不意味著隨便組裝一下,這樣以后會(huì)感到惡心,而是意味著以?xún)?yōu)雅的方式來(lái)擴(kuò)展。這樣的要求有點(diǎn)過(guò)分,哈? 我們立即采用的這樣的框架之一就是ant。從一開(kāi)始我們就可以看出,ant是由一個(gè)能理解我們構(gòu)建Java應(yīng)用的痛楚的開(kāi)發(fā)者創(chuàng)建的。從從選用ant的那一刻起,不再有javac,不再有CLASSPATH。所需的一切只是直觀(guān)的(即使有時(shí)候比較繁瑣)XML配置文件。生活(和構(gòu)建)變得簡(jiǎn)單。 隨著我們的進(jìn)展,我們開(kāi)始采用越來(lái)越多的工具。Eclipse成為我們的IDE選擇。Log4J成為我們的(也是所有其他人的)缺省的日志工具。Lucene替代了我們的商業(yè)搜索解決方案。所有這些工具都滿(mǎn)足我們的準(zhǔn)則:滿(mǎn)足需求的同時(shí),易用,易理解,易擴(kuò)展。 但仍然缺少一些東西,這些偉大的工具如ant和Eclipse,被設(shè)計(jì)用來(lái)輔助開(kāi)發(fā)軟件,或者滿(mǎn)足某一特定應(yīng)用需要,如使用Lucene進(jìn)行搜索和使用log4J來(lái)進(jìn)行記錄日志。他們都沒(méi)有解決企業(yè)應(yīng)用的核心:持久化,事務(wù)和與其他企業(yè)資源集成。 直到去年我們發(fā)現(xiàn)了令人驚奇的兩大企業(yè)武器Spring和Hibernate。這兩個(gè)框架滿(mǎn)足了幾乎所有的中間層及數(shù)據(jù)層的需求。 我們首先采用了Hibernate。它是最直觀(guān)的特性最多的ORM工具。但直到采用了Spring我們才使代碼看上去更加優(yōu)雅。使用Spring的控制反轉(zhuǎn)(Inversion of Control)我們可以?huà)仐壦械淖远x工廠(chǎng)及配置器。事實(shí)上這也是我們將Spring集成到我們應(yīng)用中的最初原因。它的wiring使我們的應(yīng)用程序更加合理化,移除了我們自制的解決方案。(每一個(gè)開(kāi)發(fā)者都會(huì)編寫(xiě)自己的框架,但是有時(shí)候必需得放棄)。 我們很快發(fā)現(xiàn)了一個(gè)額外的好處,Spring提供了對(duì)Hibernate的很容易地繼承。這使我們不必自制對(duì)Hibernate的繼承類(lèi),可以直接使用Spring的支持。這引領(lǐng)我們走進(jìn)Spring對(duì)透明持久化的支持。 如果你稍加注意的話(huà)你應(yīng)該發(fā)現(xiàn)了一個(gè)模式。越多地使用Spring,你就能發(fā)現(xiàn)越多的Spring的特性。而且我們發(fā)現(xiàn)的每一個(gè)特性都讓我們感受到樂(lè)趣。它的Web MVC框架在好幾個(gè)應(yīng)用中很好地工作。它的AOP支持在好幾個(gè)地方,特別是安全方面對(duì)我們很有幫助。它的JDBC支持對(duì)于小型程序很有幫助。對(duì)了,我們還用它來(lái)做日程。還有JNDI訪(fǎng)問(wèn)。還有email集成。談及能不能滿(mǎn)足開(kāi)發(fā)的需要,Spring可謂是雪中送炭。 我們非常喜歡Spring,并且決定應(yīng)該寫(xiě)一本關(guān)于它的書(shū)。幸運(yùn)地是,我們之中已經(jīng)有人為manning寫(xiě)過(guò)一本書(shū),知道寫(xiě)書(shū)是怎么回事。很快“應(yīng)該寫(xiě)這樣一本書(shū)的人”成為了我們。我們寫(xiě)這本書(shū)的目的是傳遞Spring的精神。Spring框架是我們工作的樂(lè)趣—我們預(yù)測(cè)也會(huì)成為您的樂(lè)趣。最后,我們希望這本書(shū)能成為你到達(dá)終點(diǎn)的快樂(lè)的交通工具。 第一部分 Spring基礎(chǔ) 第一章 快速進(jìn)入Spring世界 1.1 為什么選擇Spring? 1.1.1 J2EE開(kāi)發(fā)者的一天 1.1.2 Spring的承諾 <!--[if !supportLists]-->n <!--[endif]-->好的設(shè)計(jì)比起底層的實(shí)現(xiàn)技術(shù)更重要。 <!--[if !supportLists]-->n <!--[endif]-->通過(guò)接口松散耦合的JavaBean是很好的模型。 <!--[if !supportLists]-->n <!--[endif]-->代碼應(yīng)該易于測(cè)試。 好的設(shè)計(jì)比起底層的實(shí)現(xiàn)技術(shù)更重要。 不必為了使用EJB技術(shù)而使用它,真正需要它提供的服務(wù)才去使用它。 通過(guò)接口松散耦合的JavaBean是很好的模型 使用Spring,JavaBean通過(guò)接口依賴(lài)于協(xié)作。你所需要做的就是創(chuàng)建彼此通過(guò)接口通信的類(lèi),其他的都交給Spring來(lái)做。 代碼應(yīng)該易于測(cè)試 測(cè)試不需啟動(dòng)J2EE容器,因?yàn)槟銣y(cè)試的是POJO。 1.2 Spring是什么? 簡(jiǎn)單地講,Spring是一個(gè)輕量級(jí)的控制反轉(zhuǎn)和面向方面編程的框架。以下是這個(gè)描述的詳細(xì)解釋?zhuān)?/p> <!--[if !supportLists]-->n <!--[endif]-->輕量級(jí)。從兩個(gè)方面:大小和預(yù)處理。Spring發(fā)布版本的就是一個(gè)JAR包,大小就是1M多一點(diǎn)。經(jīng)Spring預(yù)處理的時(shí)間幾乎可以忽略。 <!--[if !supportLists]-->n <!--[endif]-->控制反轉(zhuǎn)。Spring通過(guò)控制反轉(zhuǎn)來(lái)達(dá)到松散耦合。通過(guò)IOC,對(duì)象被動(dòng)地接受他們的依賴(lài),而不是主動(dòng)地查找和創(chuàng)建他們的依賴(lài)。 <!--[if !supportLists]-->n <!--[endif]-->面向方面。Spring對(duì)AOP的支持使業(yè)務(wù)邏輯從系統(tǒng)服務(wù)中分離出來(lái)。 <!--[if !supportLists]-->n <!--[endif]-->容器。從包容和管理應(yīng)用程序?qū)ο笊芷诘囊饬x上來(lái)講,Spring是一個(gè)容器。你可以配置你的每一個(gè)JavaBean如何被創(chuàng)建,以及他們之間的關(guān)聯(lián)。不應(yīng)當(dāng)把Spring同重量級(jí)的EJB容器混淆,因?yàn)镋JB容器是重量級(jí)的而且工作起來(lái)很笨重。 <!--[if !supportLists]-->n <!--[endif]-->框架。Spring組合簡(jiǎn)單的JavaBean成復(fù)雜的的應(yīng)用程序。在Spring中應(yīng)用程序?qū)ο笸ǔMㄟ^(guò)XML文件聲明式地組合。Spring提供很多基礎(chǔ)的功能,如事務(wù)管理,持久化框架集成等,你要做的就是開(kāi)發(fā)業(yè)務(wù)邏輯。 1.2.1 Spring模塊 核心容器 Spring的核心容器提供Spring框架的基礎(chǔ)功能。在這個(gè)容器中有一個(gè)BeanFactory,它是任何基于Spring的應(yīng)用程序的核心。BeanFactory是工廠(chǎng)模式的一個(gè)實(shí)現(xiàn),他應(yīng)用IOC將應(yīng)用程序配置和依賴(lài)說(shuō)明從實(shí)際的應(yīng)用程序代碼中分離出來(lái)。 應(yīng)用程序上下文模塊 核心模塊的BeanFactory使Spring成為一個(gè)容器,而應(yīng)用程序上下文模塊使Spring成為一個(gè)框架。這個(gè)模塊擴(kuò)展了BeanFactory,增加了對(duì)I18N消息,應(yīng)用程序生命周期事件和驗(yàn)證的支持。 此外,這個(gè)模塊提供了一些企業(yè)級(jí)服務(wù),如email,JNDI訪(fǎng)問(wèn),EJB集成,遠(yuǎn)程訪(fǎng)問(wèn)和計(jì)劃任務(wù)。還包括對(duì)模板框架如Velocity和FreeMaker集成的支持。 Spring的AOP模塊 Spring在AOP模塊對(duì)AOP提供了豐富的支持。這個(gè)模塊提供了開(kāi)發(fā)自己的基于Spring的應(yīng)用程序的方面的基礎(chǔ)。 為確保Spring和其他AOP框架之間的互操作性,Spring的很多對(duì)AOP的支持都是基于AOP Alliance定義的API。AOP Alliance是一個(gè)開(kāi)源的項(xiàng)目,它的目標(biāo)是通過(guò)定義組通用的接口和組件,提高對(duì)AOP的采用率和不同AOP實(shí)現(xiàn)之間的互操作性。 Spring模塊還為Spring引入了元數(shù)據(jù)編程。通過(guò)Spring的元數(shù)據(jù)支持,你可以向源代碼中加入標(biāo)注(Annotation)來(lái)指示在何處通過(guò)何種方式來(lái)應(yīng)用方面(Aspects)。 JDBC抽象和DAO模塊 Spring的JDBC和DAO模塊使數(shù)據(jù)庫(kù)代碼更加整潔和簡(jiǎn)單,并且防止一些由于關(guān)閉數(shù)據(jù)庫(kù)資源引起失敗帶來(lái)的問(wèn)題。這個(gè)模塊還在由多個(gè)數(shù)據(jù)庫(kù)服務(wù)器給出的錯(cuò)誤消息之上建立了一層有意義的異常。 另外,這個(gè)模塊使用了Spring的AOP模塊為Spring應(yīng)用程序中的對(duì)象提供了事務(wù)管理服務(wù)。 ORM集成模塊 Spring為那些喜歡使用ORM功能的人提供了ORM模塊。Spring沒(méi)有試圖實(shí)現(xiàn)自己的ORM解決方案,只是為幾個(gè)流行的ORM框架,如Hibernate,JDO和iBATIS SQL Maps提供了外掛功能。Spring的事務(wù)管理支持這些ORM框架和JDBC。 Spring的Web模塊 Web上下文模塊建立于應(yīng)用程序上下文模塊,提供了一個(gè)適合于基于Web的應(yīng)用的上下文。此外,這個(gè)模塊提供了對(duì)一些基于Web的任務(wù)的支持,如對(duì)于文件上傳的multipart請(qǐng)求的透明處理和將請(qǐng)求參數(shù)程序化綁定到業(yè)務(wù)對(duì)象。還包含對(duì)Struts的集成的支持。 SpringMVC框架 Spring為構(gòu)建Web應(yīng)用程序帶來(lái)了一個(gè)完整特性的MVC框架。盡管Spring可以容易地與其他MVC框架如Struts集成,但是Spring的MVC框架使用IOC提供控制邏輯與業(yè)務(wù)邏輯的清晰分離。它允許你聲明式地將請(qǐng)求參數(shù)綁定到業(yè)務(wù)邏輯。還有就是Spring的MVC框架可以利用Spring的其他服務(wù),如I18N消息機(jī)制和驗(yàn)證機(jī)制。 1.3 快速上手 GreetingServiceImpl類(lèi)的greeting字段有兩個(gè)方法可以設(shè)置,通過(guò)構(gòu)造函數(shù)或?qū)傩缘膕etter方法。不明顯的是誰(shuí)來(lái)調(diào)用構(gòu)造函數(shù)或setter方法,答案是通過(guò)Spring配置文件讓Spring容器來(lái)調(diào)用。 Spring配置文件的根元素是<beans>元素。<bean>元素告訴Spring容易一個(gè)類(lèi)的信息和如何配置這個(gè)類(lèi)。<bean>元素的子元素<property>元素用來(lái)設(shè)定類(lèi)的屬性。當(dāng)然也可以通過(guò)構(gòu)造函數(shù)的入?yún)?lái)設(shè)定屬性。 1.4 理解控制反轉(zhuǎn)IOC 1.4.1 注入依賴(lài) 任何稍大一點(diǎn)的應(yīng)用程序都由多于兩個(gè)類(lèi)組成,他們相互協(xié)作完成一些業(yè)務(wù)邏輯。通常每一個(gè)對(duì)象都負(fù)責(zé)取得與它協(xié)作的那些對(duì)象的引用。你會(huì)發(fā)現(xiàn),這會(huì)導(dǎo)致高耦合,難測(cè)試的代碼。 通過(guò)應(yīng)用IoC,對(duì)象在創(chuàng)建的時(shí)候,通過(guò)調(diào)度系統(tǒng)中每一個(gè)對(duì)象的外部實(shí)體給予他們的依賴(lài)。即依賴(lài)被注入對(duì)象。即IoC的意思是關(guān)于對(duì)象如何取得他們協(xié)作的對(duì)象的依賴(lài)責(zé)任的反轉(zhuǎn)。 1.4.2 IoC實(shí)踐 問(wèn)題的所在是由于耦合。降低耦合的一個(gè)通用技術(shù)是將實(shí)現(xiàn)細(xì)節(jié)隱藏在接口后面,這樣可以交換具體實(shí)現(xiàn)而不影響客戶(hù)類(lèi)。但重要的是對(duì)象如何取得它引用的對(duì)象,如果是給予的方式,那么在測(cè)試用例中只要給予Mock對(duì)象,而在產(chǎn)品系統(tǒng)中給予真實(shí)的對(duì)象,就解決了不能單獨(dú)測(cè)試的問(wèn)題。 這就是IoC要做的事情。 創(chuàng)建應(yīng)用程序組件之間關(guān)聯(lián)的動(dòng)作被成為wiring,通常的方法是通過(guò)XML文件。 1.4.3 企業(yè)級(jí)應(yīng)用程序中的IoC 1.5 應(yīng)用面向方面編程 1.5.1 AOP簡(jiǎn)介 將這些關(guān)注方面分散于系統(tǒng)的多個(gè)組件,會(huì)對(duì)代碼引入兩個(gè)級(jí)別的復(fù)雜性: <!--[if !supportLists]-->n <!--[endif]-->實(shí)現(xiàn)系統(tǒng)范圍關(guān)注方面的代碼,在多個(gè)組件間存在重復(fù)。這意味著如果你需要修改這些關(guān)注方面的話(huà),你需要修改多個(gè)組件。即使你將這些關(guān)注方面抽象到單獨(dú)的模塊,對(duì)組件的影響只是一個(gè)方法調(diào)用,這個(gè)方法調(diào)用也會(huì)在多個(gè)組件中重復(fù)。 <!--[if !supportLists]-->n <!--[endif]-->組件中會(huì)散布一些與核心功能無(wú)關(guān)的代碼。 AOP能夠讓這些服務(wù)模塊化,然后以聲明的形式應(yīng)用于它們應(yīng)該影響的組件。這樣能夠使組件能夠更具有聚合性,專(zhuān)注于他們的關(guān)注方面,可以完全忽略需要涉及的系統(tǒng)服務(wù)。 通過(guò)AOP,可以用多層功能覆蓋核心的應(yīng)用。這些層可以靈活的聲明式地應(yīng)用于應(yīng)用程序,核心應(yīng)用程序甚至不知道他們的存在。 1.5.2 AOP實(shí)踐 織補(bǔ)Aspect 在Spring中,Aspect通過(guò)Spring的XML文件織入對(duì)象,同Bean被繞在一起的方式差不多。 1.5.3 企業(yè)級(jí)的AOP 盡管Spring的AOP可以用來(lái)分布于應(yīng)用程序核心邏輯各處的關(guān)注方面,但它的主要工作是作為Spring對(duì)聲明式事務(wù)支持的基礎(chǔ)。Spring具有幾個(gè)方面,使對(duì)JavaBeans聲明事務(wù)策略成為可能。而Acegi安全系統(tǒng)提供對(duì)JavaBeans的聲明式安全。同所有Spring配置一樣,事務(wù)和安全策略都在Spring配置文件中描述。 使用Spring的TransactionProxyFactoryBean,使我們能夠?qū)却娴腸lass監(jiān)聽(tīng)函數(shù)調(diào)用和應(yīng)用事務(wù)上下文。 1.6 Spring的代替品 EJB是一個(gè)標(biāo)準(zhǔn) EJB是有JCP定義的規(guī)范,作為標(biāo)準(zhǔn)具有一些重要的啟示: <!--[if !supportLists]-->n <!--[endif]-->廣泛的業(yè)界支持—有很多重要的廠(chǎng)商都支持這項(xiàng)技術(shù),如Sun,IBM,Oracle和BEA。這意味著很多年之內(nèi)EJB都會(huì)被支持和積極開(kāi)發(fā),這使很多公司感覺(jué)到選擇EJB作為J2EE框架比較安全。 <!--[if !supportLists]-->n <!--[endif]-->廣泛的采用—EJB技術(shù)已經(jīng)被成千上萬(wàn)的公司部署。因此EJB是大部分J2EE開(kāi)發(fā)者的工具,擁有EJB技術(shù)更容易找到工作,公司使用EJB技術(shù)也更容易招聘到技術(shù)人員。 <!--[if !supportLists]-->n <!--[endif]-->有工具支持—EJB規(guī)范是一個(gè)固定的東西,這更容易地使得廠(chǎng)商更快地生產(chǎn)工具來(lái)幫助開(kāi)發(fā)者創(chuàng)建EJB應(yīng)用程序。EJB工具具有更廣泛的選擇。 Spring和EJB的共同點(diǎn) 作為J2EE容器,EJB和Spring都為開(kāi)發(fā)者開(kāi)發(fā)應(yīng)用程序提供了強(qiáng)大的特性。下表列出了兩個(gè)框架的共同特性和它們的實(shí)現(xiàn)比較。 特性 <!--[if !supportLists]-->n <!--[endif]-->支持跨越遠(yuǎn)程方法調(diào)用的事務(wù)。 <!--[if !supportLists]-->n <!--[endif]-->自身不支持分布式事務(wù)—必需與JTA事務(wù)管理器一起使用。 <!--[if !supportLists]-->n <!--[endif]-->可以使用通配符*來(lái)針對(duì)每一個(gè)方法或每一個(gè)類(lèi)定義事務(wù)行為。 <!--[if !supportLists]-->n <!--[endif]-->不能聲明式地定義回滾行為,必需程序?qū)崿F(xiàn)。 <!--[if !supportLists]-->n <!--[endif]-->可以顯示地或通過(guò)正則表達(dá)式定義對(duì)哪些方法應(yīng)用事務(wù)行為。 <!--[if !supportLists]-->n <!--[endif]-->可以針對(duì)每一個(gè)方法或每一個(gè)異常類(lèi)型聲明式地定義回滾行為。 <!--[if !supportLists]-->n <!--[endif]-->一個(gè)建立于Spring之上的開(kāi)源的安全框架Acegi,通過(guò)Spring配置文件或類(lèi)元數(shù)據(jù)提供聲明式安全。 對(duì)于大部分的J2EE項(xiàng)目,EJB和Spring都能滿(mǎn)足其技術(shù)需求。但是也有例外—你的應(yīng)用程序也許需要遠(yuǎn)程事務(wù)調(diào)用,如果是這樣的話(huà),選擇EJB更合適。但Spring提供了對(duì)JTA事務(wù)的集成,也能滿(mǎn)足。但如果你要找的是提供聲明式事務(wù)管理和靈活的持久化引擎的J2EE框架,Spring是最好的選擇。 EJB的復(fù)雜性 EJB以下的復(fù)雜性使大家傾向于選擇輕量級(jí)的容器: <!--[if !supportLists]-->n <!--[endif]-->編寫(xiě)一個(gè)EJB太過(guò)于復(fù)雜—寫(xiě)一個(gè)EJB必需接觸至少四個(gè)文件:業(yè)務(wù)接口,home接口,bean實(shí)現(xiàn)和部署描述子。可能還牽扯到其他類(lèi),如utility類(lèi)和Value Object。相反Spring使你通過(guò)POJO定義你的實(shí)現(xiàn),和通過(guò)注入或AOP來(lái)纏繞任何額外的服務(wù)。 <!--[if !supportLists]-->n <!--[endif]-->EJB具有侵入性—為使用EJB容器提供的服務(wù),則必需使用javax.ejb接口。這使組件代碼與EJB技術(shù)綁定,使組件很難用于EJB容器之外。Spring通常不要求組件實(shí)現(xiàn)、擴(kuò)展或使用任何特定于Spring的類(lèi)或接口,是組件更具有可復(fù)用性,即使沒(méi)有Spring的存在也能使用。 <!--[if !supportLists]-->n <!--[endif]-->實(shí)體EJBs功能較弱—實(shí)體EJBs不如其他ORM工具特性多,也不夠靈活。Spring可以集成很多其他ORM框架。Value Object會(huì)導(dǎo)致重復(fù)的代碼,使用Spring和其他ORM工具,實(shí)體對(duì)象不與持久化機(jī)制耦合,可以傳遞于應(yīng)用程序的不同層。 1.6.2 考慮其他輕量級(jí)容器 類(lèi)型 下面看一下其他輕量級(jí)容器。 PicoContainer PicoContainer是一個(gè)小型的輕量級(jí)的容器,通過(guò)構(gòu)造函數(shù)和setter函數(shù)的形式提供IoC。有一個(gè)子項(xiàng)目NanoContainer通過(guò)XML和各種腳本語(yǔ)言提供對(duì)配置PicoContainer的支持。 PicoContainer的一個(gè)局限是,對(duì)于任何一個(gè)類(lèi)型,在注冊(cè)表中只允許存在一個(gè)實(shí)例。 PicoContainer只是一個(gè)容器,不提供其他Spring提供的強(qiáng)大功能,如AOP和第三方框架集成。 HiveMind HiveMind是一個(gè)相對(duì)較新的IoC容器。它也是通過(guò)構(gòu)造函數(shù)和setter函數(shù)來(lái)纏繞和配置服務(wù)。HiveMind允許用XML文件或它的簡(jiǎn)單的數(shù)據(jù)語(yǔ)言定義你的配置。 HiveMind還通過(guò)Interceptors提供類(lèi)似AOP的特性。但是沒(méi)有Spring的AOP框架強(qiáng)大。 它提供管理組件的框架,但不提供對(duì)其他技術(shù)的集成。 Avalon Avalon是第一批開(kāi)發(fā)出來(lái)的IoC容器之一。但設(shè)計(jì)中存在很多錯(cuò)誤。Avalon主要提供接口依賴(lài)的IoC。這使Avalon成為侵入式的框架。 1.6.3 Web框架 Struts Struts可以看作是Web MVC框架的事實(shí)上的標(biāo)準(zhǔn)。 最常使用的Struts類(lèi)是Action類(lèi)。Action是一個(gè)抽象類(lèi),因此你的所有處理輸入的類(lèi)都必需繼承這個(gè)類(lèi),相比之下Spring提供了Controller接口。 另一個(gè)重要的區(qū)別是二者處理表單輸入的方式。通常情況下,當(dāng)用戶(hù)提交一個(gè)Web表單時(shí),進(jìn)來(lái)的數(shù)據(jù)對(duì)應(yīng)于你的應(yīng)用程序中的一個(gè)對(duì)象。為處理表單提交,Struts要求使用ActionForm類(lèi)處理傳入的參數(shù)。這意味這你必需創(chuàng)建一個(gè)類(lèi)來(lái)將表單提交映射到你的領(lǐng)域?qū)ο?。Spring允許你直接將表單提交映射到領(lǐng)域?qū)ο蟆?/p> Struts提供內(nèi)置的聲明式表單驗(yàn)證支持。這意味著你可以通過(guò)XML文件來(lái)定義驗(yàn)證傳入的表單數(shù)據(jù)的規(guī)則。這使驗(yàn)證與業(yè)務(wù)邏輯分離,也會(huì)導(dǎo)致一些笨拙和混亂。Spring不提供聲明式驗(yàn)證,不過(guò)你可以自己集成一個(gè)驗(yàn)證框架,如Jakarta Commons Validator。 Spring可以集成Struts。 WebWork WebWork提供多視圖技術(shù)。WebWork與其他框架的最大區(qū)別是它為處理Web提交增加了一個(gè)抽象層。 WebWork提供了一個(gè)Spring沒(méi)有提供的功能:Action鏈。它允許你將一個(gè)邏輯請(qǐng)求映射到一系列的Actions。 Tapestry Tapestry與之前提到的Web框架有很大區(qū)別。Taperstry不提供基于請(qǐng)求-響應(yīng)的Servlet機(jī)制的框架。它是一個(gè)從可復(fù)用組件創(chuàng)建Web應(yīng)用的框架。 Tapestry背后的思想是,減輕開(kāi)發(fā)者思考Sessions屬性和URLs,而是以組件和方法的形式考慮Web應(yīng)用。 Tapestry不是一個(gè)使用JSPs的框架,而是代替JSPs。 1.6.4 持久化框架 第二章 纏繞Beans 創(chuàng)建對(duì)象關(guān)聯(lián)的通常辦法會(huì)導(dǎo)致難以復(fù)用和進(jìn)行單元測(cè)試的代碼。 在Spring中,組件本身不負(fù)責(zé)管理與其他組件的關(guān)聯(lián)。相反,對(duì)寫(xiě)作組件的引用是由容器給他們的。創(chuàng)建應(yīng)用程序組件之間關(guān)聯(lián)的動(dòng)作成為纏繞(Wiring)。 2.1 包含Beans 2.1.1 介紹BeanFactory Bean工廠(chǎng)知道應(yīng)用程序中的很多對(duì)象,在實(shí)例化對(duì)象時(shí),它能夠創(chuàng)建協(xié)作對(duì)象之間的關(guān)聯(lián)關(guān)系。這移除了Bean本身和它的客戶(hù)端的配置負(fù)擔(dān)。因此,當(dāng)Bean工廠(chǎng)分發(fā)對(duì)象時(shí),這些對(duì)象是已經(jīng)完全配置好的,已經(jīng)知道了他們協(xié)作的對(duì)象,已經(jīng)可以供使用。 Spring中有很多BeanFactory的實(shí)現(xiàn),但最有用的是org.springframework.beans.factory.xmlBeanFactory,它基于包含在XML文件中的定義來(lái)加載Beans。 為創(chuàng)建XmlBeanFactory,向構(gòu)造函數(shù)傳遞一個(gè)java.io.InputStream。這個(gè)InputStream向工廠(chǎng)提供XML文件。Beans以懶加載的方式加載到Beans工廠(chǎng)中,Bean工廠(chǎng)會(huì)立即加載Bean的定義,但只要當(dāng)這些Beans被使用的時(shí)候才會(huì)被實(shí)例化。 為從BeanFactory獲取Bean,只要調(diào)用getBean()方法,向它傳遞你要獲取的bean的名稱(chēng)。當(dāng)調(diào)用getBean()時(shí),工廠(chǎng)會(huì)實(shí)例化Bean并開(kāi)始使用依賴(lài)注入來(lái)設(shè)置Bean的屬性。 2.1.2 使用應(yīng)用程序上下文 <!--[if !supportLists]-->n <!--[endif]-->提供解析文本消息的方法,包括對(duì)這些消息提供I18N支持。 <!--[if !supportLists]-->n <!--[endif]-->提供加載文件資源,如圖片,的通用方法。 <!--[if !supportLists]-->n <!--[endif]-->可以向注冊(cè)為監(jiān)聽(tīng)者的Beans發(fā)布事件。 因?yàn)锳pplicationContext的額外功能,幾乎在所有的應(yīng)用程序中都優(yōu)先使用ApplicationContext,而非BeanFactory。 ApplicationContext的實(shí)現(xiàn)有很多,而常用的有三個(gè): <!--[if !supportLists]-->n <!--[endif]-->ClassPathXmlApplicationContext—從在classpath中定位的XML文件加載一個(gè)上下文定義,將上下文定義文件作為classpath資源處理。 <!--[if !supportLists]-->n <!--[endif]-->FileSystemXmlApplicationContext—從文件系統(tǒng)中的XML文件加載上下文定義。 <!--[if !supportLists]-->n <!--[endif]-->XmlWebApplicationContext—從包含于Web應(yīng)用中的XML文件加載上下文定義。 獲取Bean的方式同BeanFactory,可以使用getBean()方法,因?yàn)锳pplicationContext繼承了BeanFactory接口。 應(yīng)用程序上下文與Bean工廠(chǎng)的另一個(gè)比較大的區(qū)別是他們加載單例Beans的方式。BeanFactory懶加載所有Beans,而ApplicationContext預(yù)先加載所有的單例Beans。 2.1.3 Bean的生命周期 <!--[if !supportLists]-->1. <!--[endif]-->容器找到Bean的定義并實(shí)例化Bean。 <!--[if !supportLists]-->2. <!--[endif]-->Spring使用依賴(lài)注入產(chǎn)生Bean定義中指定的所有的屬性。 <!--[if !supportLists]-->3. <!--[endif]-->如果Bean實(shí)現(xiàn)了BeanNameAware接口,工廠(chǎng)調(diào)用setBeanName()方法,傳遞Bean的ID。 <!--[if !supportLists]-->4. <!--[endif]-->如果Bean實(shí)現(xiàn)了BeanFactoryAware接口,工廠(chǎng)調(diào)用setBeanFactory方法,并傳遞工廠(chǎng)本身的實(shí)例。 <!--[if !supportLists]-->5. <!--[endif]-->如果存在與Bean關(guān)聯(lián)的BeanPostProcessors,則調(diào)用他們的postProcessBeforeInitialization方法。 <!--[if !supportLists]-->6. <!--[endif]-->如果指定了Bean的init方法,則調(diào)用這個(gè)方法。 <!--[if !supportLists]-->7. <!--[endif]-->最后,如果存在與Bean關(guān)聯(lián)的BeanPostProcessors,則調(diào)用他們的postProcessAfterInitialization方法。 從這時(shí)候起,Bean可以被應(yīng)用程序使用,并一直存在與Bean工廠(chǎng)中,直到不需要它。有兩種方式將Bean從Bean工廠(chǎng)中移除。 <!--[if !supportLists]-->1. <!--[endif]-->如果Bean實(shí)現(xiàn)了DisposableBean接口,調(diào)用destroy()方法。 <!--[if !supportLists]-->2. <!--[endif]-->如果指定了自定義的destroy方法,則調(diào)用這個(gè)方法。 處在Spring應(yīng)用程序上下文中的Bean與BeanFactory中的唯一區(qū)別是,如果Bean實(shí)現(xiàn)了ApplicationContextAware接口,則調(diào)用它的setApplicationContext方法。 2.2基本的纏繞(Basic Wiring) 2.2.1使用XML纏繞 上下文定義文件的根節(jié)點(diǎn)是<beans>元素,這個(gè)元素具有一個(gè)或多個(gè)<bean>元素作為子元素。每一個(gè)<bean>元素都定義一個(gè)配置到Spring容器中的JavaBean。 2.2.2 增加一個(gè)Bean 原型vs單例 缺省情況下,所有Spring中的Beans都是單例的。如果每一次都要請(qǐng)求一個(gè)特定的實(shí)例的話(huà),可以定義一個(gè)原型Bean。將<bean>元素的singleton屬性設(shè)置為false就可以將Bean定義為原型Bean。 初始化與銷(xiāo)毀 使用init-method屬性指定的方法在Bean實(shí)例化之后立即執(zhí)行。destroy-method屬性指定的方法在Bean從容器移除之前調(diào)用。Spring提供了兩個(gè)接口,可以執(zhí)行相同的功能:InitializingBean和DisposableBean。前者提供了一個(gè)方法afterPropertiesSet(),在Bean的所有屬性設(shè)置后執(zhí)行,后者提供的方法destroy()在Bean從容器中移除時(shí)調(diào)用。使用接口的好處是不需要配置,但是也會(huì)將Bean與Spring的API綁定起來(lái)。 2.2.3 通過(guò)setter方法注入依賴(lài) 簡(jiǎn)單的Bean的配置 使用<property>元素的子元素<value>元素可以來(lái)給Bean設(shè)置原始類(lèi)型的屬性。原始類(lèi)型的屬性的設(shè)定無(wú)須指定類(lèi)型,Spring會(huì)自動(dòng)進(jìn)行類(lèi)型轉(zhuǎn)換。 引用其他Beans 使用<property>元素的子元素<ref>元素來(lái)引用其他Beans。 內(nèi)部的Beans 可以將<bean>元素直接嵌入到<property>元素。這種纏繞方式的缺點(diǎn)是被嵌入的元素不能在別處使用。而且影響XML文件的可讀性。 纏繞集合 Spring支持很多種集合類(lèi)型作為Bean屬性,如下表所示。 XML 纏繞集合是使用上表中提到的XML元素,而不是使用<value>或<ref>。 纏繞lists和arrays List的元素可以是<value>,<ref>,<bean>或其他<list>。 纏繞集合 集合可以保證元素的唯一性。 纏繞maps Map中的每一個(gè)記錄都用一個(gè)<entry>元素定義。<entry>的key屬性限定為只能是String類(lèi)型。 纏繞Properties 使用<props>元素纏繞,每一個(gè)<props>元素的子元素都是用一個(gè)<prop>元素定義。 設(shè)定null值 使用<null/>元素。 Setter注入的一個(gè)代替方式 通過(guò)構(gòu)造函數(shù)注入。 2.2.4 通過(guò)構(gòu)造函數(shù)注入依賴(lài) 處理模糊的構(gòu)造函數(shù)參數(shù) 如果出現(xiàn)模糊的情況,Spring會(huì)拋出org.springframework.beans.factory.UnsatisfiedDependencyException。有兩種方法來(lái)解決這個(gè)問(wèn)題,使用下標(biāo)或類(lèi)型。 使用<constructor-arg>的index屬性指定參數(shù)的下標(biāo)。還可以使用type屬性指定參數(shù)的類(lèi)型。 如何選擇:使用構(gòu)造函數(shù)還是使用setter方法? 這是一個(gè)有爭(zhēng)議的問(wèn)題。 支持使用構(gòu)造函數(shù)注入的理由: <!--[if !supportLists]-->n <!--[endif]-->強(qiáng)制執(zhí)行一個(gè)強(qiáng)依賴(lài)協(xié)議。簡(jiǎn)單的說(shuō),Bean如果沒(méi)得到所有的依賴(lài)就不會(huì)被實(shí)例化。 <!--[if !supportLists]-->n <!--[endif]-->既然所有的依賴(lài)都可以通過(guò)構(gòu)造函數(shù)設(shè)定,使用setter方法比較多余。 <!--[if !supportLists]-->n <!--[endif]-->只允許通過(guò)構(gòu)造函數(shù)設(shè)定函數(shù),可以讓這些屬性不可更改(immutable)。 支持setter方法注入依賴(lài)的理由: <!--[if !supportLists]-->n <!--[endif]-->如果依賴(lài)比較多,構(gòu)造函數(shù)的參數(shù)會(huì)比較長(zhǎng)。 <!--[if !supportLists]-->n <!--[endif]-->如果構(gòu)造可用的對(duì)象的方法有多個(gè)的話(huà),就會(huì)有多個(gè)構(gòu)造函數(shù)。 <!--[if !supportLists]-->n <!--[endif]-->如果構(gòu)造函數(shù)的多個(gè)參數(shù)的類(lèi)型一樣的話(huà),很難確定參數(shù)的目的是什么。 <!--[if !supportLists]-->n <!--[endif]-->構(gòu)造函數(shù)注入使自身不具有好的集成性。 2.3 自動(dòng)纏繞(autowiring) 有四種類(lèi)型的自動(dòng)纏繞: <!--[if !supportLists]-->n <!--[endif]-->byName—在容器中查找與指定的名稱(chēng)相同的Bean。 <!--[if !supportLists]-->n <!--[endif]-->byType—在容器中查找一個(gè)唯一的與指定類(lèi)型相同的Bean。如果沒(méi)有找到則不纏繞,如果找到多個(gè),拋出org.springframework.beans.factory.UnsatisfiedDependencyException。 <!--[if !supportLists]-->n <!--[endif]-->constructor—在容器中查找與被纏繞的Bean的構(gòu)造函數(shù)匹配的Bean,使用指定的構(gòu)造函數(shù)的參數(shù)。 <!--[if !supportLists]-->n <!--[endif]-->autodetect—試圖先使用constructor,然后使用byType方法。 2.3.1 處理自動(dòng)纏繞的模糊性 2.3.2 混合使用自動(dòng)纏繞和顯式纏繞 2.3.3 缺省自動(dòng)纏繞 2.3.4 自動(dòng)纏繞還是不自動(dòng)纏繞 2.4 使用Spring的特殊Beans <!--[if !supportLists]-->n <!--[endif]-->可以通過(guò)后置處理配置文件來(lái)參與Bean和Bean的工廠(chǎng)的生命周期。 <!--[if !supportLists]-->n <!--[endif]-->從外部屬性文件加載配置信息。 <!--[if !supportLists]-->n <!--[endif]-->改變Spring的依賴(lài)注入,在配置Bean屬性的時(shí)候,自動(dòng)將String類(lèi)型轉(zhuǎn)換成其他類(lèi)型。 <!--[if !supportLists]-->n <!--[endif]-->加載文本消息,包括國(guó)際化的消息。 <!--[if !supportLists]-->n <!--[endif]-->監(jiān)聽(tīng)或響應(yīng)由其他Beans或容器發(fā)布的應(yīng)用程序事件。 <!--[if !supportLists]-->n <!--[endif]-->使其意識(shí)到自己在Spring容器中的身份。 2.4.1 后置處理Beans 書(shū)寫(xiě)B(tài)ean后置處理器 定義一個(gè)類(lèi)實(shí)現(xiàn)BeanPostProcessor接口。 注冊(cè)Bean后置處理器 如果應(yīng)用程序運(yùn)行于Bean工廠(chǎng),則需要使用程序來(lái)進(jìn)行注冊(cè),調(diào)用工廠(chǎng)的addBeanPostProcessor方法,將上面定義的類(lèi)作為參數(shù)傳入。如果應(yīng)用程序運(yùn)行于應(yīng)用程序上下文,只需要將定義的類(lèi)注冊(cè)為一個(gè)Bean。容器會(huì)將其識(shí)別為后置處理器并應(yīng)用于所有的Beans。 Spring自帶的Bean后置處理器 例如,ApplicationContextAwareProcessor對(duì)所有實(shí)現(xiàn)ApplicationContextAware接口的Beans設(shè)置應(yīng)用程序上下文。 DefaultAdvisorAutoProxyCreator基于容器中所有的候選advisors創(chuàng)建AOP代理。 2.4.2 后置處理Bean工廠(chǎng) 有兩個(gè)比較有用的實(shí)現(xiàn)。PropertyPlaceHolderConfigurer從一個(gè)或多個(gè)外部屬性文件中加載屬性,并用這些屬性填充Bean纏繞XML文件中的占位符變量。CustomEditorConfigurer讓你注冊(cè)java.beans.PropertyEditor來(lái)將屬性纏繞值翻譯成其他屬性值。 2.4.3 使配置外部化 如果使用ApplicationContext作為Spring容器的話(huà),外部化屬性是很容易的??梢允褂肞ropertyPlaceHolderConfigurer來(lái)告訴Spring從外部屬性文件來(lái)加載特定的配置。如果你的配置分解為多個(gè)外部屬性文件的話(huà),使用PropertyPlaceHolderConfigurer的locations屬性來(lái)設(shè)置<list>元素。 2.4.4 定制屬性編輯器 Spring自帶了幾個(gè)自定義編輯器: <!--[if !supportLists]-->n <!--[endif]-->URLEditor—將String對(duì)象纏繞到URL對(duì)象。 <!--[if !supportLists]-->n <!--[endif]-->ClassEditor <!--[if !supportLists]-->n <!--[endif]-->CustomDateEditor <!--[if !supportLists]-->n <!--[endif]-->FileEditor <!--[if !supportLists]-->n <!--[endif]-->LocaleEditor <!--[if !supportLists]-->n <!--[endif]-->StringArrayPropertyEditor <!--[if !supportLists]-->n <!--[endif]-->StringTrimmerEditor 2.4.5 解析文本消息 一般情況下,通過(guò)ApplicationContext的getMessage方法來(lái)訪(fǎng)問(wèn)消息,也可以在JSP中使用<spring:message>標(biāo)簽來(lái)訪(fǎng)問(wèn)。 2.4.6 監(jiān)聽(tīng)事件 <!--[if !supportLists]-->n <!--[endif]-->ContextClosedEvent—當(dāng)應(yīng)用程序上下文關(guān)閉的時(shí)候發(fā)布。 <!--[if !supportLists]-->n <!--[endif]-->ContextRefreshedEvent—當(dāng)應(yīng)用程序上下文初始化或刷新的時(shí)候發(fā)布。 <!--[if !supportLists]-->n <!--[endif]-->RequestHandledEvent—當(dāng)請(qǐng)求已經(jīng)被處理的時(shí)候,在Web應(yīng)用上下文環(huán)境內(nèi)發(fā)布。 如果你想要Bean對(duì)應(yīng)用程序事件作出響應(yīng),只需實(shí)現(xiàn)org.springframework.context.ApplicationListener接口。 2.4.7 發(fā)布事件 2.4.8 讓Beans有意識(shí) 知道你是誰(shuí) BeanNameAware的setBeanName()方法。 知道你在哪生存 Spring的BeanFactoryAware和ApplicationContextAware接口讓Beans意識(shí)到容器的存在。 第三章 創(chuàng)建方面(Aspects) 一個(gè)方面(Aspect)是你實(shí)現(xiàn)的一個(gè)橫切的功能。它是你模塊化的應(yīng)用程序的一個(gè)方面或領(lǐng)域。最常見(jiàn)的例子是寫(xiě)日志(logging)。 連接點(diǎn)(joinpoint) 一個(gè)連接點(diǎn)是應(yīng)用程序執(zhí)行過(guò)程中的一個(gè)點(diǎn),在這個(gè)點(diǎn)可以插入一個(gè)方面(Aspect)。這個(gè)點(diǎn)可以是調(diào)用方法,可以是拋出異常,也可以是修改一個(gè)字段。在這些點(diǎn),方面的代碼可以插入到應(yīng)用程序的正常流程來(lái)增加新的行為。 修訂(Advice) Advice是方面的實(shí)際實(shí)現(xiàn)。以logging為例,logging advice包含包含實(shí)際實(shí)現(xiàn)loggin的代碼。Advice在jointpoints插入到應(yīng)用程序中。 切入點(diǎn)(Pointcut) 切入點(diǎn)定義應(yīng)該應(yīng)用何種連接點(diǎn)Advice。Advice可以應(yīng)用于AOP框架支持的任何連接點(diǎn)。 引入(Introduction) Introduction允許你向既存的類(lèi)增加方法或?qū)傩浴?/p> 目標(biāo)(Target) Target是被修訂的類(lèi)??梢允悄銓?xiě)的類(lèi),也可以是第三方的類(lèi)。 代理(Proxy) 代理是向目標(biāo)對(duì)象應(yīng)用修訂后創(chuàng)建的對(duì)象。就客戶(hù)端對(duì)象而言,目標(biāo)對(duì)象和代理對(duì)象是相同的。 編織(Weaving) 編織是向目標(biāo)對(duì)象應(yīng)用方面來(lái)創(chuàng)建一個(gè)新的代理的對(duì)象的過(guò)程。方面在指定的連接點(diǎn)被織入目標(biāo)對(duì)象。編織可以發(fā)生在目標(biāo)類(lèi)的生命周期的多個(gè)點(diǎn): <!--[if !supportLists]-->n <!--[endif]-->編譯期—方面在目標(biāo)類(lèi)被編譯時(shí)織入。這需要一個(gè)特殊的編譯器。 <!--[if !supportLists]-->n <!--[endif]-->類(lèi)加載期—方面在目標(biāo)類(lèi)被加載進(jìn)JVM時(shí)織入。這需要一個(gè)特殊的ClassLoader來(lái)增強(qiáng)目標(biāo)類(lèi)的字節(jié)碼。 <!--[if !supportLists]-->n <!--[endif]-->運(yùn)行時(shí)—方面在應(yīng)用程序執(zhí)行的某個(gè)時(shí)刻被織入。 Advice包含需要應(yīng)用的橫切行為。 3.1.2 Spring的AOP實(shí)現(xiàn) 切入點(diǎn)通常以XML形式寫(xiě)在Spring配置文件中。其他框架,如AspectJ,要求必需使用一種特殊的語(yǔ)法來(lái)定義方面和切入點(diǎn)。 Spring在運(yùn)行時(shí)修訂對(duì)象 Spring只有在代理Bean在應(yīng)用程序中被需要時(shí)才創(chuàng)建代理對(duì)象。Spring有兩種方式可以產(chǎn)生代理類(lèi)。如果你的目標(biāo)對(duì)象實(shí)現(xiàn)了暴露了必要方法的接口,則Spring使用JDK的java.lang.reflect.Proxy接口。這個(gè)類(lèi)允許Spring動(dòng)態(tài)地創(chuàng)建新類(lèi)來(lái)實(shí)現(xiàn)必要的接口,織入任何修訂,和代理任何從這些接口的對(duì)目標(biāo)類(lèi)的方法調(diào)用。如果你的目標(biāo)類(lèi)不實(shí)現(xiàn)接口,Spring使用CGLIB來(lái)產(chǎn)生目標(biāo)類(lèi)的一個(gè)子類(lèi)。創(chuàng)建子類(lèi)時(shí),Spring織入修訂,并使用子類(lèi)代理所有到目標(biāo)類(lèi)的調(diào)用。 Spring實(shí)現(xiàn)AOP Alliance接口 AOP Alliance是一個(gè)都對(duì)用Java實(shí)現(xiàn)AOP感興趣的多方的聯(lián)合項(xiàng)目。他們的目標(biāo)是標(biāo)準(zhǔn)化Java AOP接口來(lái)提供不同Java的AOP實(shí)現(xiàn)的互操作性。這意味著實(shí)現(xiàn)他們接口的AOP修訂會(huì)在任何與AOP Alliance兼容的框架中復(fù)用。 Spring只提供方法連接點(diǎn) 這會(huì)有礙于創(chuàng)建細(xì)粒度的修訂,如監(jiān)聽(tīng)對(duì)對(duì)象字段的更新。 3.2 創(chuàng)建修訂(Advice) 修訂類(lèi)型 3.2.1 Before修訂 3.2.2 After修訂 3.2.3 Around修訂 3.2.4 Throws修訂 3.2.5 引入(Introduction)修訂 3.3 定義切入點(diǎn) 3.3.1 在Spring中定義切入點(diǎn) ClassFilter是根據(jù)類(lèi)來(lái)過(guò)濾方面,而MathodFilter是根據(jù)方法來(lái)過(guò)濾。 3.3.2 理解修訂(advisors) 3.3.3 使用Spring的靜態(tài)切入點(diǎn) NameMatchMethodPointcut 根據(jù)提供的名稱(chēng)來(lái)匹配方法名,提供的名稱(chēng)可以帶通配符。 正則表達(dá)式切入點(diǎn) RegexpMethodPointcut使你可以使用正則表達(dá)式來(lái)定義切入點(diǎn)。 3.3.4 使用動(dòng)態(tài)切入點(diǎn) 3.3.5 切入點(diǎn)操作 3.4 創(chuàng)建介紹(Introductions) 3.4.1 實(shí)現(xiàn)IntroductionInterceptor 3.4.2 創(chuàng)建IntroductionAdvisor 3.4.3 謹(jǐn)慎使用Introduction Advice 3.5 使用ProxyFactoryBean 屬性 最經(jīng)常使用的屬性是target,proxyInterfaces和interceptorNames。 3.6 自動(dòng)代理 3.6.1 BeanNameAutoProxyCreator 3.6.2 DefaultAdvisorAutoProxyCreator 3.6.3 元數(shù)據(jù)自動(dòng)代理 第二部分 Spring的業(yè)務(wù)層 第四章 數(shù)據(jù)庫(kù)相關(guān)問(wèn)題 Spring提供在它的所有DAO框架中一直的異常層次結(jié)構(gòu)。 4.1.1 理解Spring的DataAccessException 你不必一定要處理DataAccessException DataAccessException是一個(gè)RuntimeException,因此它是unchecked的異常。這意味著,當(dāng)持久化層拋出這些異常時(shí),你的代碼不一定要處理。這遵循了Spring的通用理念:checked異常會(huì)導(dǎo)致很多不必要的catch或throws語(yǔ)句,使代碼比較凌亂。 DataAccessException是從Spring的NestedRuntimeException繼承,根Exception可以從NestedRuntimeException的getClause方法獲得。 Spring對(duì)異常進(jìn)行分類(lèi) 如下表所示,Spring有豐富的異常層次: 異常 4.1.2 使用DataSources 從JNDI取得DataSource Spring應(yīng)用程序通常運(yùn)行于J2EE應(yīng)用服務(wù)器或Tomcat之類(lèi)的Web服務(wù)器。這些服務(wù)器可以通過(guò)JNDI提供DataSource。在Spring中我們處理這件事情也是通過(guò)Spring bean。在這種情況下,我們使用JndiObjectFactoryBean。 創(chuàng)建DataSource連接池 如果我們的Spring容器運(yùn)行的環(huán)境不存在DataSource,而我們又希望使用連接池的好處,我們只需實(shí)現(xiàn)一個(gè)連接池bean,實(shí)現(xiàn)DataSource接口即可。一個(gè)很好的例子是Jakarta Commons DBCP項(xiàng)目的BasicDataSource類(lèi)。只需在Spring配置文件中配置一個(gè)bean即可。 測(cè)試時(shí)使用DataSource Spring自帶了一個(gè)輕量級(jí)的DataSource實(shí)現(xiàn),用于單元測(cè)試或單元測(cè)試套件。 4.1.3 一致性DAO Support 模板回調(diào)設(shè)計(jì)的頂部有一個(gè)DAO Support類(lèi),你的DAO類(lèi)可以從這些類(lèi)繼承。 4.2 在Spring中使用JDBC 代碼比較冗贅。 4.2.2 使用JdbcTemplate 寫(xiě)數(shù)據(jù) PreparedStatementCreator接口的實(shí)現(xiàn)負(fù)責(zé)創(chuàng)建一個(gè)PreparedStatement,這個(gè)接口只提供了一個(gè)方法createPreparedStatement。 這個(gè)接口的實(shí)現(xiàn)通常也實(shí)現(xiàn)另一個(gè)接口:SqlProvider。通過(guò)實(shí)現(xiàn)這個(gè)類(lèi)的方法—getSql()—使你的類(lèi)向JdbcTemplate類(lèi)提供SQL字符串。 對(duì)PreparedStatementCreator的補(bǔ)充是PreparedStatementSetter。實(shí)現(xiàn)這個(gè)接口的類(lèi)接受一個(gè)PreparedStatement參數(shù),負(fù)責(zé)設(shè)定所有的參數(shù)。 如果要更新多行,可以使用BatchPrepareStatementSetter。如果你的JDBC驅(qū)動(dòng)支持批量操作,更新會(huì)批量進(jìn)行,創(chuàng)建高效的數(shù)據(jù)庫(kù)訪(fǎng)問(wèn),否則Spring會(huì)模擬批處理,但是語(yǔ)句會(huì)一個(gè)一個(gè)執(zhí)行。 讀數(shù)據(jù) 我們要告訴Spring如何處理ResultSet中的每一行,通過(guò)實(shí)現(xiàn)RowCallbackHandler的方法processRow來(lái)處理。 如果通過(guò)查詢(xún)獲取多個(gè)對(duì)象,可以實(shí)現(xiàn)一個(gè)子接口。例如,如果要獲取某個(gè)類(lèi)的所有對(duì)象,可以實(shí)現(xiàn)ResultReader接口。Spring提供了這個(gè)接口的一個(gè)實(shí)現(xiàn),RowMapperResultReader。RowMapper接口負(fù)責(zé)將ResultSet的一行映射到一個(gè)對(duì)象。 JdbcTemplate也提供了很多返回結(jié)果為int或String等簡(jiǎn)單類(lèi)型的方法。 調(diào)用存儲(chǔ)過(guò)程 Spring提供對(duì)存儲(chǔ)過(guò)程的支持是通過(guò)實(shí)現(xiàn)接口CallableStatementCallBack。 4.2.3 將操作創(chuàng)建為對(duì)象 創(chuàng)建SqlUpdate對(duì)象 為創(chuàng)建執(zhí)行插入或更新的可復(fù)用對(duì)象,繼承SqlUpdate類(lèi)。 使用MappingSqlQuery查詢(xún)數(shù)據(jù)庫(kù) 通過(guò)繼承MappingSqlQuery來(lái)將一個(gè)查詢(xún)建模為對(duì)象。 4.2.4 自動(dòng)遞增鍵值 4.3 Spring的ORM框架支持簡(jiǎn)介 4.4 在Spring中集成Hibernate 4.4.2 管理Hibernate資源 4.4.3 通過(guò)HibernateTemplate訪(fǎng)問(wèn)Hibernate 4.4.4 繼承HibernateDaoSupport 4.5.1 配置JDO 在Spring中,使用LocalePersistenceManagerFactoryBean配置PersistenceManagerFactoryBean。有了JDO PersistenceManagerFactory,下一步就是將此bean纏繞到JdoTemplate。 4.5.2 使用JdoTemplate訪(fǎng)問(wèn)數(shù)據(jù) 4.6 Spring與iBATIS 4.6.2 使用SqlMapClientTemplate 4.7 Spring與OJB 4.7.1 設(shè)置OJB的PersistenceBroker 第五章 管理事務(wù) <!--[if !supportLists]-->n <!--[endif]-->Atomic—事務(wù)由一個(gè)或多個(gè)活動(dòng)綁定起來(lái)作為一個(gè)工作單元。 <!--[if !supportLists]-->n <!--[endif]-->Consistent—一旦事務(wù)結(jié)束,系統(tǒng)的狀態(tài)與業(yè)務(wù)狀態(tài)一致。 <!--[if !supportLists]-->n <!--[endif]-->Isolated—事務(wù)應(yīng)當(dāng)允許多個(gè)用戶(hù)同是工作而不會(huì)相互添亂。 <!--[if !supportLists]-->n <!--[endif]-->Durable—事務(wù)結(jié)束后,事務(wù)的結(jié)果應(yīng)當(dāng)持久化。 5.1.2 理解Spring的事務(wù)管理支持 5.1.3 Spring事務(wù)管理器簡(jiǎn)介 事務(wù)管理器實(shí)現(xiàn) 為使用事務(wù)管理器,必需在你的應(yīng)用程序上下文中聲明這個(gè)事務(wù)管理器。 JDBC事務(wù) 如果使用JDBC做應(yīng)用程序的持久化,DataSourceTransactionManager可以為你處理事務(wù)邊界。使用XML纏繞到應(yīng)用程序上下文中。 Hibernate事務(wù) 如果你的應(yīng)用程序使用Hibernate進(jìn)行持久化,可以使用HibernateTransactionManager可以為你處理事務(wù)。HibernateTransactionManager將事務(wù)管理的職責(zé)代理到net.sf.hibernate.Transaction對(duì)象。 JDO(Java Data Object)事務(wù) JdoTransactionManager。 OJB事務(wù) PersistenceBrokerTransactionManager。 JTA事務(wù) JtaTransactionManager。如果使用多個(gè)數(shù)據(jù)源,需要使用JtaTransactionManager。 5.2 Spring中的程序式事務(wù) 如果你想完全控制事務(wù)的邊界,使用程序式事務(wù)比較好。通常你不需要如此精確的邊界,這就是為什么通常在應(yīng)用程序代碼之外聲明事務(wù)。 5.3 聲明事務(wù) 5.3.1 理解事務(wù)屬性 <!--[if !supportLists]-->n <!--[endif]-->傳播行為 <!--[if !supportLists]-->n <!--[endif]-->隔離級(jí)別 <!--[if !supportLists]-->n <!--[endif]-->只讀提示 <!--[if !supportLists]-->n <!--[endif]-->事務(wù)有效時(shí)間 傳播行為 傳播行為定義了相對(duì)于客戶(hù)端和被調(diào)用方法的事務(wù)的邊界。Spring定義了七種傳播行為,如下表所示: 傳播行為 隔離級(jí)別 事務(wù)的隔離級(jí)別如下表所示: 隔離級(jí)別 并不是所有的資源管理器都支持所有的這些隔離級(jí)別。 只讀 聲明事務(wù)是只讀的,給予底層數(shù)據(jù)存儲(chǔ)機(jī)會(huì)對(duì)事務(wù)進(jìn)行優(yōu)化。 事務(wù)超時(shí) 設(shè)定事務(wù)過(guò)一定時(shí)間后自動(dòng)回滾。 5.3.2 聲明簡(jiǎn)單的事務(wù)策略 MatchAlwaysTransactionAttributeSource是最簡(jiǎn)單的TransactionAttributeSource的實(shí)現(xiàn),當(dāng)調(diào)用其getTransactionAttribute()方法時(shí),不管事務(wù)中纏繞的是哪一個(gè)方法,每次都返回相同的TransactionAttribute(PROPAGATION_REQUIRED和ISOLATION_DEFAULT)。 改變?nèi)笔〉腡ransactionAttribute 缺省策略是PROPAGATION_REQUIRED和ISOLATION_DEFAULT,如果要改變的話(huà),只需對(duì)transactionAttribute纏繞另一個(gè)TransactionAttribute。 5.4 根據(jù)方法名聲明事務(wù) 5.4.1 使用NameMatchTransactionAttributeSource 事務(wù)屬性描述子的格式: PROPAGATION,ISOLATION,readOnly,-Exceptions,+Exceptions 5.4.2 簡(jiǎn)化名稱(chēng)匹配的事務(wù) 5.5 使用元數(shù)據(jù)聲明事務(wù) <bean id="transactionAttributeSource" class="org.springframework.transaction.interceptor. AttributesTransactionAttributeSource"> <constructor-arg> <ref bean="attributesImpl"/> </constructor-arg> </bean> 注意這里的構(gòu)造函數(shù)參數(shù)attributesImpl,事務(wù)屬性源使用它與底層的元數(shù)據(jù)實(shí)現(xiàn)進(jìn)行交互。 5.5.2 使用Commons Attribute聲明事務(wù) <bean id="attributesImpl" class="org.springframework. metadata.commons.CommonsAttributes"> ... </bean> 標(biāo)記事務(wù)性方法 Jakarta Commons Attributes通過(guò)替換方法/類(lèi)前面的注釋中doclet標(biāo)簽來(lái)實(shí)現(xiàn)。 設(shè)置Commons Attributes的構(gòu)建 Jakarta Commons Attributes的機(jī)制是預(yù)編譯你的代碼中的doclet標(biāo)簽,重寫(xiě)你的類(lèi),在代碼中嵌入元數(shù)據(jù)。需要在構(gòu)建文件中加入與編譯器。 5.6 裁剪事務(wù)聲明 5.6.2 自動(dòng)代理事務(wù) 第六章 遠(yuǎn)程訪(fǎng)問(wèn) 客戶(hù)端調(diào)用代理,好像是代理在提供服務(wù)功能,代理代表客戶(hù)端同遠(yuǎn)程服務(wù)通信。在服務(wù)器端,可以使用以上六種模型將任何Spring管理的Bean的功能暴露為遠(yuǎn)程服務(wù)。 6.2 使用RMI 6.2.2 導(dǎo)出RMI服務(wù) 首先書(shū)寫(xiě)服務(wù)接口,無(wú)須從java.rmi.Remote繼承。然后定義這個(gè)接口的實(shí)現(xiàn)類(lèi)。下面就可以將這個(gè)實(shí)現(xiàn)類(lèi)配置成Spring配置文件中的<bean>。然后使用RmiServiceExporter將此實(shí)現(xiàn)導(dǎo)出成服務(wù)。 RMI有很多局限性。首先,跨防火墻工作比較困難。因?yàn)镽MI的通信使用任意端口,典型的防火墻都不允許這樣做。另外,RMI是基于Java的。這意味著服務(wù)器端和客戶(hù)端都必須使用Java編寫(xiě)。 6.3 使用Hessian和Burlap進(jìn)行遠(yuǎn)程訪(fǎng)問(wèn) Hessian和Burlap的唯一區(qū)別就是基于二進(jìn)制消息還是基于XML。 6.3.1 訪(fǎng)問(wèn)Hessian和Burlap服務(wù) 6.3.2 使用Hessian/Burlap暴露Bean功能 使用HessianServiceExporter。 配置Hessian控制器 RmiServiceExporter與HessianServiceExporter之間的另一個(gè)主要區(qū)別是,因?yàn)镠essian是基于HTTP的,HessianServiceExporter實(shí)現(xiàn)為一個(gè)Spring MVC控制器。這意味著如果要導(dǎo)出Hessian服務(wù),需要執(zhí)行兩個(gè)額外的配置步驟: <!--[if !supportLists]-->1. <!--[endif]-->在Spring配置文件中配置一個(gè)URL處理器,來(lái)對(duì)合適的Hessian服務(wù)bean分派Hessian服務(wù)URLs。 <!--[if !supportLists]-->2. <!--[endif]-->在web.xml中配置一個(gè)DispatcherServlet,將應(yīng)用程序部署為一個(gè)Web應(yīng)用程序。 將URL模式映射到Hessian服務(wù)。 導(dǎo)出Burlap服務(wù) 使用BurlapServiceExporter。 6.4 使用HTTP Invoker 6.4.1 通過(guò)HTTP訪(fǎng)問(wèn)服務(wù) 6.4.2 將beans暴露為HTTP服務(wù) HTTP Invoker的局限性在于,它的服務(wù)端和客戶(hù)端都必須是基于Spring的。 6.5 使用EJB Spring提供了兩個(gè)代理工廠(chǎng)beans,來(lái)對(duì)EJBs進(jìn)行代理:LocalStatelessSessionProxyFactoryBean和SimpleRemoteStatelessSessionProxyFactoryBean。 <bean id="paymentService" class="org.springframework.ejb. access.LocalStatelessSessionProxyFactoryBean" lazy-init="true"> <property name="jndiName"> <value>payService</value> </property> <property name="businessInterface"> <value>com.springinaction.payment.PaymentService</value> </property> </bean> 纏繞EJBs 與纏繞其他Spring beans相同。 工作機(jī)制 首先,在啟動(dòng)的時(shí)候,LocalStatelessSessionProxyFactoryBean使用由jndiName屬性指定的JNDI名稱(chēng)來(lái)通過(guò)JNDI查找EJB的本地home接口。然后,每次調(diào)用接口方法的時(shí)候,代理都調(diào)用本地home接口的create()方法獲取EJB的引用。最后,代理調(diào)用EJB相應(yīng)的方法。 訪(fǎng)問(wèn)遠(yuǎn)程EJB 使用SimpleRemoteStatelessSessionProxyFactoryBean。 6.5.2 開(kāi)發(fā)支持Spring的EJBs <!--[if !supportLists]-->n <!--[endif]-->AbstractMessageDrivenBean—對(duì)于開(kāi)發(fā)消息驅(qū)動(dòng)的beans比較有用。不接受來(lái)自JMS的消息。 <!--[if !supportLists]-->n <!--[endif]-->AbstractJmsMessageDrivenBean—接受來(lái)自JMS的消息。 <!--[if !supportLists]-->n <!--[endif]-->AbstractStatefulSessionBean <!--[if !supportLists]-->n <!--[endif]-->AbstractStatelessSessionBean 6.6 使用基于JAX-RPC的Web services 6.6.1 引用JAX Web service 6.6.2 在Spring中纏繞web服務(wù) 第七章 訪(fǎng)問(wèn)企業(yè)服務(wù) Spring的JNDI抽象,使你能夠在你的應(yīng)用程序配置文件中聲明JNDI查詢(xún),從而可以將這些對(duì)象纏繞到其他beans的屬性中,好像JNDI對(duì)象也是POJO。 7.1.1 使用通常的JNDI 7.1.2 代理JNDI對(duì)象 7.2 發(fā)送E-mail <!--[if !supportLists]-->n <!--[endif]-->CosMailSenderImpl—SMTP郵件發(fā)送器的簡(jiǎn)單實(shí)現(xiàn),基于Jason Hunter的COS實(shí)現(xiàn)。 <!--[if !supportLists]-->n <!--[endif]-->JavaMailSenderImpl—基于JavaMail API的實(shí)現(xiàn)。允許發(fā)送MIME消息。 <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> <property name="host"> <value>mail.springtraining.com</value> </property> </bean> 還可以定義郵件模板。 7.3 定時(shí)任務(wù) 7.3.1 使用Java的Timer進(jìn)行定時(shí) 通過(guò)繼承java.util.TimerTask類(lèi)。 對(duì)定時(shí)任務(wù)作出計(jì)劃 ScheduledTimerTask定義定時(shí)任務(wù)執(zhí)行的頻率。 啟動(dòng)計(jì)時(shí)器 TimerFactoryBean。 缺陷是,只能設(shè)定運(yùn)行的頻率,而不能精確設(shè)定每天何時(shí)運(yùn)行定時(shí)任務(wù)。 7.3.2 使用quartz定時(shí)器 創(chuàng)建一個(gè)Job 繼承Spring的QuartzJobBean。 計(jì)劃job Quartz的Trigger類(lèi)用來(lái)決定何時(shí)以何種方式運(yùn)行Quartz Job。Spring自帶了兩個(gè)觸發(fā)器:SimpleTriggerBean和CronTriggerBean。 啟動(dòng)job SchedulerFactoryBean。 7.3.3 按計(jì)劃調(diào)用方法 7.4 使用JMS發(fā)送消息 7.4.1 使用JMS模板發(fā)送消息 使用模板 使用JmsTemplate來(lái)發(fā)送消息。 纏繞模板 使用JMS1.0.2 區(qū)別是JmsTemplate102需要知道是在使用點(diǎn)對(duì)點(diǎn)還是發(fā)布訂閱模式。缺省情況下,JmsTemplate102假定你使用點(diǎn)對(duì)點(diǎn)模式。 處理JMS異常 不必處理JMSException。 7.4.2 處理消息 7.4.3 轉(zhuǎn)化消息 第三部分 Spring的web層 過(guò)程開(kāi)始于客戶(hù)端發(fā)送一個(gè)請(qǐng)求,接受這個(gè)請(qǐng)求的第一個(gè)組件是Spring的DispatcherServlet。同大部分基于Java的MVC框架一樣,Spring MVC使用一個(gè)唯一的前端控制器來(lái)處理request。在Spring MVC中DispatcherServlet就是前端控制器。負(fù)責(zé)處理請(qǐng)求的Spring MVC組件是controller,為了知道哪一個(gè)controller應(yīng)該處理某個(gè)請(qǐng)求,DispatcherServlet查詢(xún)一個(gè)或多個(gè)HandlerMapping。HandlerMapping的工作就是將URL模式映射到Controller對(duì)象。一旦DispatcherServlet得到Controller對(duì)象,它將request分派給這個(gè)控制器來(lái)執(zhí)行業(yè)務(wù)邏輯。執(zhí)行完業(yè)務(wù)邏輯后,Controller返回一個(gè)ModelAndView對(duì)象給DispatcherServlet。ModelAndView對(duì)象可以包含一個(gè)View對(duì)象或者View對(duì)象的名稱(chēng)。如果包含的是View對(duì)象的名稱(chēng),DispatcherServlet通過(guò)ViewResolver來(lái)查找對(duì)應(yīng)的View對(duì)象。最后,DispatcherServlet將request分派給View對(duì)象,View對(duì)象負(fù)責(zé)產(chǎn)生一個(gè)響應(yīng)到客戶(hù)端。 8.1.2 配置DispatcherServlet 分解應(yīng)用程序上下文 可以將各層的xml文件分開(kāi)。便于維護(hù)。為保證所有這些配置文件都會(huì)被加載,必需在web.xml文件中配置上下文加載器。有兩個(gè)可供選擇的加載器:ContextLoaderListener和ContextLoaderServlet。 8.1.3 Spring MVC核心 實(shí)現(xiàn)Controller接口,handleRequest方法。 配置Controller Bean DispatcherServlet使用的缺省處理器映射是BeanNameUrlHandlerMapping。 聲明view resolver View resolver的工作是將返回的ModelAndView中的view名稱(chēng)映射為view對(duì)象。對(duì)于產(chǎn)生JSP的views,最簡(jiǎn)單的莫過(guò)于使用InternalResourceViewResolver。 <bean id="viewResolver" class="org.springframework.web. servlet.view.InternalResourceViewResolver"> <property name="prefix"> <value>/WEB-INF/jsp/</value> </property> <property name="suffix"> <value>.jsp</value> </property> </bean> 將view名稱(chēng)映射為/WEB-INF/jsp/[view名稱(chēng)].jsp 創(chuàng)建JSP 所有的組件放在一起工作 8.2 將requests映射到controllers <!--[if !supportLists]-->n <!--[endif]-->BeanNameUrlHandlerMapping—基于控制器的名稱(chēng)將控制器映射到URLs。 <!--[if !supportLists]-->n <!--[endif]-->SimpleUrlHandlerMapping—使用在上下文配置文件中定義的屬性集合將控制器映射到URLs。 <!--[if !supportLists]-->n <!--[endif]-->CommonsPathMapHandlerMapping—使用controller代碼中的元數(shù)據(jù)將控制器映射到URLs。 8.2.1 將URLs映射到bean名稱(chēng) 8.2.2 使用SimpleUrlHandlerMapping 8.2.3 使用元數(shù)據(jù)映射controllers 8.2.4 使用多個(gè)HandlerMapping 8.3 使用Controllers處理請(qǐng)求 類(lèi)型 AbstractController AbstractCommandController SimpleFormController 8.3.1 編寫(xiě)簡(jiǎn)單的控制器 ModelAndView簡(jiǎn)介 ModelAndView對(duì)象完全封裝view和model數(shù)據(jù)。 纏繞Controller 8.3.2 處理命令 Handler方法還接受一個(gè)Command參數(shù)。 8.3.3 處理form提交 驗(yàn)證輸入 Org.springframework.validation.Validator接口為Spring MVC提供驗(yàn)證。 8.3.4 使用wizard處理復(fù)雜表單 擴(kuò)展AbstractWizardFormController類(lèi)。 頁(yè)面跳轉(zhuǎn) getTargetPage() 結(jié)束wizard 使用特殊的請(qǐng)求參數(shù)“_finish”。 取消wizard 增加取消按鈕,使用“_cancel”參數(shù)。 每次驗(yàn)證一個(gè)表單 控制器的validatePage()方法,可以針對(duì)每個(gè)頁(yè)面進(jìn)行驗(yàn)證。 8.3.5 使用一個(gè)控制器處理多個(gè)動(dòng)作 根據(jù)URL模式確定方法名。 解決方法名稱(chēng) MultiActionController基于方法名稱(chēng)resolver來(lái)解決方法名稱(chēng)。缺省的方法名稱(chēng)解決器是InternalPathMethodNameResolver。Spring提供了另外兩種:ParameterMethodNameResolver和PropertiesMethodNameResolver。 8.3.6 使用Throwaway控制器 8.4 解決views Spring提供了ViewResolver的四個(gè)實(shí)現(xiàn): <!--[if !supportLists]-->n <!--[endif]-->InternalResourceViewResolver—將邏輯view名稱(chēng)解決為View對(duì)象,這些對(duì)象是使用模板文件資源生成。(如JSPs和Velocity模板) <!--[if !supportLists]-->n <!--[endif]-->BeanNameViewResolver—將邏輯view名稱(chēng)解決為DispatcherServlet的應(yīng)用程序上下文中的View Beans。 <!--[if !supportLists]-->n <!--[endif]-->ResourceBundlerViewResolver—將邏輯View名稱(chēng)解決為包含在ResourceBundle中的View對(duì)象。 <!--[if !supportLists]-->n <!--[endif]-->XmlViewResolver—從XML文件解決View beans,這個(gè)XML文件是與DispatcherServlet的應(yīng)用程序上下文分開(kāi)的。 8.4.1 使用模板views 8.4.2 解決view beans 在分離的XML文件中聲明view beans。 XmlFileViewResolver。 從Resouce bundle解決view ResourceBundleViewResolver。 8.4.3 選擇View Resolver 使用多個(gè)View Resolver 可以使用多個(gè),使用order屬性設(shè)定優(yōu)先級(jí)。 8.5 使用Spring的綁定標(biāo)簽 8.6 處理異常 第九章 View層的其他選擇 9.1.2 配置Velocity引擎 9.1.3 解決Velocity view 9.1.4 格式化日期和數(shù)字 9.1.5 暴露request和session屬性 9.1.6 在Velocity中綁定表單字段 9.2 使用FreeMaker 9.2.1 構(gòu)造FreeMaker view 9.2.2 配置FreeMaker引擎 9.2.3 解決FreeMaker view 9.2.4 在FreeMaker中綁定表單字段 9.3 使用Tiles設(shè)計(jì)頁(yè)面布局 配置Tiles TilesConfigurer。 解決Tiles view 使用InternalResourceViewResolver即可,只要將viewClass設(shè)定為T(mén)ilesView。它將邏輯view名稱(chēng)解決為T(mén)iles定義。 9.3.2 Tiles控制器 9.4 產(chǎn)生非HTML輸出 9.4.2 產(chǎn)生PDF文檔 9.4.3 產(chǎn)生其他非HTML文件 第十章 使用其他Web框架 10.1.2 實(shí)現(xiàn)可訪(fǎng)問(wèn)Spring的Struts Action 10.1.3 代理Actions 將Actions纏繞為Spring beans 將Actions注冊(cè)為Spring beans。讓代理來(lái)找Actions。 使用request代理 DelegatingRequestProcessor。 10.2 使用Tapestry 10.3 與JSF集成 10.4 與WebWork集成 第十一章 Spring應(yīng)用程序的安全性 11.1 Acegi安全系統(tǒng)簡(jiǎn)介 Acegi使用監(jiān)聽(tīng)request的servlet過(guò)濾器來(lái)執(zhí)行認(rèn)證和實(shí)施安全性。 Acegi可以在較低級(jí)別上通過(guò)保證方法調(diào)用的安全實(shí)施安全性。通過(guò)使用Spring AOP,Acegi代理對(duì)象,應(yīng)用方面來(lái)保證用戶(hù)具有適當(dāng)?shù)氖跈?quán)來(lái)調(diào)用安全方法。 11.1.1 安全監(jiān)聽(tīng)器 11.1.2 認(rèn)證管理器 11.1.3 訪(fǎng)問(wèn)決策管理器 11.1.4 運(yùn)行管理器 11.2 管理認(rèn)證 public interface AuthenticationManager { public Authentication authenticate(Authentication authentication) throws AuthenticationException; } Authenticate方法接受一個(gè)Authentication對(duì)象,并試圖認(rèn)證用戶(hù)。如果認(rèn)證成功,則返回一個(gè)完整的Authentication對(duì)象,包含用戶(hù)的授權(quán)信息。如果認(rèn)證失敗,則拋出AuthenticationException異常。Acegi提供了AuthenticationManager的一個(gè)實(shí)現(xiàn):ProviderManager。 11.2.1 配置ProviderManager <bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager"> <property name="providers"> <list> <ref bean="daoAuthenticationProvider"/> <ref bean="passwordDaoProvider"/> </list> </property> </bean> 通過(guò)providers屬性給予ProviderManager一組認(rèn)證提供者。認(rèn)證提供者由net.sf.acegisecurity.provider.AuthenticationProvider接口定義。Spring提供了這個(gè)接口的多個(gè)實(shí)現(xiàn),如下表所示: 認(rèn)證提供者 AuthByAdapterProvider CasAuthenticationProvider DaoAuthenticationProvider JaasAuthenticationProvider PasswordDaoAuthenticationProvider RemoteAuthenticationProvider RunAsImplAuthenticationProvider TestingAuthenticationProvider 11.2.2 使用數(shù)據(jù)庫(kù)認(rèn)證 聲明DAO認(rèn)證提供者 <bean id="authenticationProvider" class="net.sf.acegisecurity. providers.dao.DaoAuthenticationProvider"> <property name="authenticationDao"> <ref bean="authenticationDao"/> </property> </bean> authenticationDao屬性標(biāo)識(shí)用來(lái)從數(shù)據(jù)庫(kù)獲取用戶(hù)信息的bean。這個(gè)屬性應(yīng)當(dāng)是net.sf.acegisecurity.providers.dao.AuthenticationDao。Acegi提供了AuthenticationDao的兩個(gè)實(shí)現(xiàn):InMemoryDaoImpl和JdbcDaoImpl。 使用內(nèi)存Dao <bean id="authenticationDao" class="net.sf.acegisecurity. providers.dao.memory.InMemoryDaoImpl"> <property name="userMap"> <value> palmerd=4moreyears,ROLE_PRESIDENT bauerj=ineedsleep,ROLE_FIELD_OPS,ROLE_DIRECTOR myersn=traitor,disabled,ROLE_FIELD_OPS </value> </property> </bean> 局限性在于必需編輯Spring配置文件并重新部署應(yīng)用來(lái)管理安全。 聲明JDBC DAO <bean id="authenticationDao" class="net.sf.acegisecurity.providers.dao.jdbc.JdbcDaoImpl"> <property name="dataSource"> <ref bean="dataSource"/> </property> </bean> JdbcDaoImpl假定你在數(shù)據(jù)庫(kù)中使用特定的表來(lái)存儲(chǔ)用戶(hù)信息,特別地,使用“Users”表和“Authorities”表。但可以使用usersByUserNameQuery來(lái)設(shè)定如何獲取用戶(hù)信息。JdbcDaoImpl還提供了usersByUserNameMapping屬性來(lái)引用MappingSqlQuery接口。這個(gè)接口將ResultSet轉(zhuǎn)換成UserDetails對(duì)象。 同樣可以通過(guò)設(shè)定authoritiesByUserNameQuery和authoritiesByUserNameMapping屬性來(lái)獲取授權(quán)信息。 使用加密密碼 Acegi提供了三個(gè)密碼解碼器: <!--[if !supportLists]-->n <!--[endif]-->PlainTextPasswordEncoder(缺?。?-不執(zhí)行任何解碼。 <!--[if !supportLists]-->n <!--[endif]-->Md5PasswordEncoder—執(zhí)行MD5解碼。 <!--[if !supportLists]-->n <!--[endif]-->ShaPasswordEncoder—執(zhí)行安全哈希算法解碼。 可以通過(guò)更改DaoAuthenticationProvider的passwordEncoder屬性來(lái)設(shè)定解碼器。還可以對(duì)解碼器使用salt source,即密鑰(encrytion key)。Acegi提供了兩個(gè)salt sources:ReflectionSaltSource和SystemWideSaltSource。 緩存用戶(hù)信息 DaoAuthenticationProvider通過(guò)UserCache接口的實(shí)現(xiàn)來(lái)支持用戶(hù)信息的緩存。 public interface UserCache { public UserDetails getUserFromCache(String username); public void putUserInCache(UserDetails user); public void removeUserFromCache(String username); } Acegi提供了兩種實(shí)現(xiàn):NullUserCache和EhCacheBasedUserCache。前者不執(zhí)行任何緩存。后者基于開(kāi)源的ehcache項(xiàng)目。 <bean id="userCache" class="net.sf.acegisecurity. providers.dao.cache.EhCacheBasedUserCache"> <property name="minutesToIdle">15</property> </bean> minutesToIdle設(shè)定在沒(méi)有讀取緩存的情況下,用戶(hù)信息應(yīng)當(dāng)駐留多長(zhǎng)時(shí)間。 11.2.3 使用LDAP中心庫(kù)進(jìn)行認(rèn)證 <bean id="authenticationProvider" class="net.sf.acegisecurity. providers.dao.PasswordDaoAuthenticationProvider"> <property name="passwordAuthenticationDao"> <ref bean="passwordAuthenticationDao"/> </property> </bean> 這個(gè)Dao屬性應(yīng)當(dāng)實(shí)現(xiàn)PasswordAuthenticationDao接口。Acegi不提供這個(gè)接口的實(shí)現(xiàn),但是在它的sandbox中有LdapPasswordAuthenticationDao實(shí)現(xiàn)。 11.2.4 使用Acegi和Yale CAS支持單點(diǎn)登錄 11.3 控制訪(fǎng)問(wèn) public interface AccessDecisionManager { public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config) throws AccessDeniedException; public boolean supports(ConfigAttribute attribute); public boolean supports(Class clazz); } Supports方法考量資源的類(lèi)型和配置屬性(安全資源的訪(fǎng)問(wèn)必需條件),據(jù)此決定訪(fǎng)問(wèn)決策管理器是否可以對(duì)資源做出訪(fǎng)問(wèn)決策。Decide方法是作出最終決策的地方,如果它正常返回不拋出異常的話(huà),則授權(quán)對(duì)安全資源的訪(fǎng)問(wèn)。否則,拒絕訪(fǎng)問(wèn)。 11.3.1 投票訪(fǎng)問(wèn)決策 <!--[if !supportLists]-->n <!--[endif]-->net.sf.acegisecurity.vote.AffirmativeBased <!--[if !supportLists]-->n <!--[endif]-->net.sf.acegisecurity.vote.ConsensusBased <!--[if !supportLists]-->n <!--[endif]-->net.sf.acegisecurity.vote.UnanimousBased 這三者的區(qū)別之處在于訪(fǎng)問(wèn)決策管理器如何作出最后的決策,如下表所示: 訪(fǎng)問(wèn)決策管理器 例如,以下對(duì)UnanimousBased的配置: <bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.UnanimousBased"> <property name="decisionVoters"> <list> <ref bean="roleVoter"/> </list> </property> </bean> 11.3.2 決定如何投票 public interface AccessDecisionVoter { public static final int ACCESS_GRANTED = 1; public static final int ACCESS_ABSTAIN = 0; public static final int ACCESS_DENIED = -1; public boolean supports(ConfigAttribute attribute); public boolean supports(Class clazz); public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config); } Acegi提供了RoleVoter實(shí)現(xiàn)。處理基于角色的安全資源配置。 11.3.3 處理角色禁止 11.4 保護(hù)Web應(yīng)用 過(guò)濾器 當(dāng)請(qǐng)求提交到Acegi保護(hù)的Web應(yīng)用時(shí),請(qǐng)求被傳遞給所有Acegi的過(guò)濾器。 11.4.1 代理Acegi過(guò)濾器 在web.xml中只包含對(duì)FilterToBeanProxy的聲明。最后,還要將過(guò)濾器與URL模式關(guān)聯(lián)。 11.4.2 執(zhí)行Web安全性 使用過(guò)濾器安全截獲器 11.4.3 處理login 11.5 方法調(diào)用安全性 |
|
|