天天看點

《Spring實戰(第4版)》——第1章 Spring之旅 1.1簡化Java開發

本節書摘來自異步社群《spring實戰(第4版)》一書中的第1章,第1.1節,作者: 【美】craig walls(沃爾斯)著,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視

spring可以做很多事情,它為企業級開發提供給了豐富的功能,但是這些功能的底層都依賴于它的兩個核心特性,也就是依賴注入(dependency injection,di)和面向切面程式設計(aspect-oriented programming,aop)。

作為本書的開始,在第1章“spring之旅”中,我将快速介紹一下spring架構,包括spring di和aop的概況,以及它們是如何幫助讀者解耦應用元件的。

在第2章“裝配bean”中,我們将深入探讨如何将應用中的各個元件拼裝在一起,讀者将會看到spring所提供的自動配置、基于java的配置以及xml配置。

在第3章“進階裝配”中,将會告别基礎的内容,為讀者展現一些最大化spring威力的技巧和技術,包括條件化裝配、處理自動裝配時的歧義性、作用域以及spring表達式語言。

在第4章“面向切面的spring”中,展示如何使用spring的aop特性把系統級的服務(例如安全和審計)從它們所服務的對象中解耦出來。本章也為後面的第9章、第13章和第14章做了鋪墊,這幾章将會分别介紹如何将spring aop用于聲明式安全以及緩存。

本章内容:

spring的bean容器

介紹spring的核心子產品

更為強大的spring生态系統

spring的新功能

對于java程式員來說,這是一個很好的時代。

在java近20年的曆史中,它經曆過很好的時代,也經曆過飽受诟病的時代。盡管有很多粗糙的地方,如applet、企業級javabean(enterprise javabean,ejb)、java資料對象(java data object,jdo)以及無數的日志架構,但是作為一個平台,java的曆史是豐富多彩的,有很多的企業級軟體都是基于這個平台建構的。spring是java曆史中很重要的組成部分。

在誕生之初,建立spring的主要目的是用來替代更加重量級的企業級java技術,尤其是ejb。相對于ejb來說,spring提供了更加輕量級和簡單的程式設計模型。它增強了簡單老式java對象(plain old java object,pojo)的功能,使其具備了之前隻有ejb和其他企業級java規範才具有的功能。

随着時間的推移,ejb以及java 2企業版(java 2 enterprise edition,j2ee)在不斷演化。ejb自身也提供了面向簡單pojo的程式設計模型。現在,ejb也采用了依賴注入(dependency injection,di)和面向切面程式設計(aspect-oriented programming,aop)的理念,這毫無疑問是受到spring成功的啟發。

盡管j2ee(現在稱之為jee)能夠趕上spring的步伐,但spring也沒有停止前進。spring繼續在其他領域發展,而jee則剛剛開始涉及這些領域,或者還完全沒有開始在這些領域的創新。移動開發、社交api內建、nosql資料庫、雲計算以及大資料都是spring正在涉足和創新的領域。spring的前景依然會很美好。

正如我之前所言,對于java開發者來說,這是一個很好的時代。

本書會對spring進行研究,在這一章中,我們将會在較為宏觀的層面上介紹spring,讓你對spring是什麼有直覺的體驗。本章将讓讀者對spring所解決的各類問題有一個清晰的認識,同時為其他章奠定基礎。

spring是一個開源架構,最早由rod johnson建立,并在《expert one-on-one:j2ee design and development》這本著作中進行了介紹。spring是為了解決企業級應用開發的複雜性而建立的,使用spring可以讓簡單的javabean實作之前隻有ejb才能完成的事情。但spring不僅僅局限于伺服器端開發,任何java應用都能在簡單性、可測試性和松耦合等方面從spring中獲益。

bean的各種名稱……雖然spring用bean或者javabean來表示應用元件,但并不意味着spring元件必須要遵循javabean規範。一個spring元件可以是任何形式的pojo。在本書中,我采用javabean的廣泛定義,即pojo的同義詞。

縱覽全書,讀者會發現spring 可以做非常多的事情。但歸根結底,支撐spring的僅僅是少許的基本理念,所有的理念都可以追溯到spring最根本的使命上:簡化java開發。

這是一個鄭重的承諾。許多架構都聲稱在某些方面做了簡化,但spring的目标是緻力于全方位的簡化java開發。這勢必引出更多的解釋,spring是如何簡化java開發的?

為了降低java開發的複雜性,spring采取了以下4種關鍵政策:

基于pojo的輕量級和最小侵入性程式設計;

通過依賴注入和面向接口實作松耦合;

基于切面和慣例進行聲明式程式設計;

通過切面和模闆減少樣闆式代碼。

幾乎spring所做的任何事情都可以追溯到上述的一條或多條政策。在本章的其他部分,我将通過具體的案例進一步闡述這些理念,以此來證明spring是如何完美兌現它的承諾的,也就是簡化java開發。讓我們先從基于pojo的最小侵入性程式設計開始。

1.1.1 激發pojo的潛能

如果你從事java程式設計有一段時間了,那麼你或許會發現(可能你也實際使用過)很多架構通過強迫應用繼承它們的類或實作它們的接口進而導緻應用與架構綁死。一個典型的例子是ejb 2時代的無狀态會話bean。早期的ejb是一個很容易想到的例子,不過這種侵入式的程式設計方式在早期版本的struts、webwork、tapestry以及無數其他的java規範和架構中都能看到。

spring竭力避免因自身的api而弄亂你的應用代碼。spring不會強迫你實作spring規範的接口或繼承spring規範的類,相反,在基于spring建構的應用中,它的類通常沒有任何痕迹表明你使用了spring。最壞的場景是,一個類或許會使用spring注解,但它依舊是pojo。

不妨舉個例子,請參考下面的helloworldbean類:

程式清單1.1 spring不會在helloworldbean上有任何不合理的要求

《Spring實戰(第4版)》——第1章 Spring之旅 1.1簡化Java開發

可以看到,這是一個簡單普通的java類——pojo。沒有任何地方表明它是一個spring元件。spring的非侵入程式設計模型意味着這個類在spring應用和非spring應用中都可以發揮同樣的作用。

盡管形式看起來很簡單,但pojo一樣可以具有魔力。spring賦予pojo魔力的方式之一就是通過di來裝配它們。讓我們看看di是如何幫助應用對象彼此之間保持松散耦合的。

1.1.2 依賴注入

依賴注入這個詞讓人望而生畏,現在已經演變成一項複雜的程式設計技巧或設計模式理念。但事實證明,依賴注入并不像它聽上去那麼複雜。在項目中應用di,你會發現你的代碼會變得異常簡單并且更容易了解和測試。

di功能是如何實作的

任何一個有實際意義的應用(肯定比hello world示例更複雜)都會由兩個或者更多的類組成,這些類互相之間進行協作來完成特定的業務邏輯。按照傳統的做法,每個對象負責管理與自己互相協作的對象(即它所依賴的對象)的引用,這将會導緻高度耦合和難以測試的代碼。

舉個例子,考慮下程式清單1.2所展現的knight類。

程式清單1.2 damselrescuingknight隻能執行rescuedamselquest探險任務

《Spring實戰(第4版)》——第1章 Spring之旅 1.1簡化Java開發

可以看到,<code>damselrescuingknight</code>在它的構造函數中自行建立了<code>rescue damselquest</code>。這使得<code>damselrescuingknight</code>緊密地和<code>rescuedamselquest</code>耦合到了一起,是以極大地限制了這個騎士執行探險的能力。如果一個少女需要救援,這個騎士能夠召之即來。但是如果一條惡龍需要殺掉,或者一個圓桌……額……需要滾起來,那麼這個騎士就愛莫能助了。

更糟糕的是,為這個<code>damselrescuingknight</code>編寫單元測試将出奇地困難。在這樣的一個測試中,你必須保證當騎士的<code>embarkonquest()</code>方法被調用的時候,探險的embark()方法也要被調用。但是沒有一個簡單明了的方式能夠實作這一點。很遺憾,<code>damselrescuingknight</code>将無法進行測試。

耦合具有兩面性(two-headed beast)。一方面,緊密耦合的代碼難以測試、難以複用、難以了解,并且典型地表現出“打地鼠”式的bug特性(修複一個bug,将會出現一個或者更多新的bug)。另一方面,一定程度的耦合又是必須的——完全沒有耦合的代碼什麼也做不了。為了完成有實際意義的功能,不同的類必須以适當的方式進行互動。總而言之,耦合是必須的,但應當被小心謹慎地管理。

通過di,對象的依賴關系将由系統中負責協調各對象的第三方元件在建立對象的時候進行設定。對象無需自行建立或管理它們的依賴關系,如圖1.1所示,依賴關系将被自動注入到需要它們的對象當中去。

《Spring實戰(第4版)》——第1章 Spring之旅 1.1簡化Java開發

圖1.1 依賴注入會将所依賴的關系自動交給目标對象,而不是讓對象自己去擷取依賴

為了展示這一點,讓我們看一看程式清單1.3中的<code>braveknight</code>,這個騎士不僅勇敢,而且能挑戰任何形式的探險。

程式清單1.3 braveknight足夠靈活可以接受任何賦予他的探險任務

《Spring實戰(第4版)》——第1章 Spring之旅 1.1簡化Java開發

我們可以看到,不同于之前的<code>damselrescuingknight</code>,<code>braveknight</code>沒有自行建立探險任務,而是在構造的時候把探險任務作為構造器參數傳入。這是依賴注入的方式之一,即構造器注入(constructor injection)。

更重要的是,傳入的探險類型是quest,也就是所有探險任務都必須實作的一個接口。是以,<code>braveknight</code>能夠響應<code>rescuedamselquest</code>、 <code>slaydragonquest</code>、 <code>makeroundtablerounderquest</code>等任意的quest實作。

這裡的要點是<code>braveknight</code>沒有與任何特定的quest實作發生耦合。對它來說,被要求挑戰的探險任務隻要實作了quest接口,那麼具體是哪種類型的探險就無關緊要了。這就是di所帶來的最大收益——松耦合。如果一個對象隻通過接口(而不是具體實作或初始化過程)來表明依賴關系,那麼這種依賴就能夠在對象本身毫不知情的情況下,用不同的具體實作進行替換。

對依賴進行替換的一個最常用方法就是在測試的時候使用<code>mock</code>實作。我們無法充分地測試<code>damselrescuingknight</code>,因為它是緊耦合的;但是可以輕松地測試<code>braveknigh</code>t,隻需給它一個<code>quest</code>的<code>mock</code>實作即可,如程式清單1.4所示。

程式清單1.4 為了測試braveknight,需要注入一個mock quest

《Spring實戰(第4版)》——第1章 Spring之旅 1.1簡化Java開發

你可以使用mock架構mockito去建立一個<code>quest</code>接口的mock實作。通過這個mock對象,就可以建立一個新的<code>braveknight</code>執行個體,并通過構造器注入這個mock <code>quest</code>。當調用<code>embarkonquest()</code>方法時,你可以要求<code>mockito</code>架構驗證<code>quest</code>的mock實作的<code>embark()</code>方法僅僅被調用了一次。

将quest注入到knight中

現在<code>braveknight</code>類可以接受你傳遞給它的任意一種<code>quest</code>的實作,但該怎樣把特定的<code>quest</code>實作傳給它呢?假設,希望<code>braveknight</code>所要進行探險任務是殺死一隻怪龍,那麼程式清單1.5中的<code>slaydragonquest</code>也許是挺合适的。

程式清單1.5 slaydragonquest是要注入到braveknight中的quest實作

《Spring實戰(第4版)》——第1章 Spring之旅 1.1簡化Java開發

我們可以看到,<code>slaydragonquest</code>實作了<code>quest</code>接口,這樣它就适合注入到<code>braveknight</code>中去了。與其他的java入門樣例有所不同,<code>slaydragonquest</code>沒有使用<code>system.out.println()</code>,而是在構造方法中請求一個更為通用的<code>printstream</code>。這裡最大的問題在于,我們該如何将<code>slaydragonquest</code>交給<code>braveknight</code>呢?又如何将<code>printstream</code>交給<code>slaydragonquest</code>呢?

建立應用元件之間協作的行為通常稱為裝配(wiring)。spring有多種裝配bean的方式,采用xml是很常見的一種裝配方式。程式清單1.6展現了一個簡單的spring配置檔案:knights.xml,該配置檔案将<code>braveknight</code>、<code>slaydragonquest</code>和<code>printstream</code>裝配到了一起。

程式清單1.6 使用spring将slaydragonquest注入到braveknight中

《Spring實戰(第4版)》——第1章 Spring之旅 1.1簡化Java開發

在這裡,<code>braveknight</code>和<code>slaydragonquest</code>被聲明為spring中的bean。就<code>braveknight bean</code>來講,它在構造時傳入了對<code>slaydragonquest bean</code>的引用,将其作為構造器參數。同時,<code>slaydragonquest bean</code>的聲明使用了spring表達式語言(spring expression language),将<code>system.out</code>(這是一個<code>printstream</code>)傳入到了<code>slaydragonquest</code>的構造器中。

如果xml配置不符合你的喜好的話,spring還支援使用java來描述配置。比如,程式清單1.7展現了基于java的配置,它的功能與程式清單1.6相同。

程式清單1.7 spring提供了基于java的配置,可作為xml的替代方案

《Spring實戰(第4版)》——第1章 Spring之旅 1.1簡化Java開發

不管你使用的是基于xml的配置還是基于java的配置,di所帶來的收益都是相同的。盡管<code>braveknight</code>依賴于<code>quest</code>,但是它并不知道傳遞給它的是什麼類型的<code>quest</code>,也不知道這個<code>quest</code>來自哪裡。與之類似,<code>slaydragonquest</code>依賴于<code>printstream</code>,但是在編碼時它并不需要知道這個<code>`printstream</code>是什麼樣子的。隻有spring通過它的配置,能夠了解這些組成部分是如何裝配起來的。這樣的話,就可以在不改變所依賴的類的情況下,修改依賴關系。

這個樣例展現了在spring中裝配bean的一種簡單方法。謹記現在不要過多關注細節。第2章我們會深入講解spring的配置檔案,同時還會了解spring裝配bean的其他方式,甚至包括一種讓spring自動發現bean并在這些bean之間建立關聯關系的方式。

現在已經聲明了<code>braveknight</code>和<code>quest</code>的關系,接下來我們隻需要裝載xml配置檔案,并把應用啟動起來。

觀察它如何工作

spring通過應用上下文(application context)裝載bean的定義并把它們組裝起來。spring應用上下文全權負責對象的建立群組裝。spring自帶了多種應用上下文的實作,它們之間主要的差別僅僅在于如何加載配置。

因為knights.xml中的bean是使用xml檔案進行配置的,是以選擇<code>classpathxmlapplicationcontext</code>[1]作為應用上下文相對是比較合适的。該類加載位于應用程式類路徑下的一個或多個xml配置檔案。程式清單1.8中的<code>main()</code>方法調用<code>classpathxmlapplicationcontext</code>加載knights.xml,并獲得<code>knight</code>對象的引用。

程式清單1.8 knightmain.java加載包含knight的spring上下文

《Spring實戰(第4版)》——第1章 Spring之旅 1.1簡化Java開發

這裡的<code>main()</code>方法基于knights.xml檔案建立了spring應用上下文。随後它調用該應用上下文擷取一個id為knight的bean。得到<code>knight</code>對象的引用後,隻需簡單調用<code>embarkonquest()</code>方法就可以執行所賦予的探險任務了。注意這個類完全不知道我們的英雄騎士接受哪種探險任務,而且完全沒有意識到這是由<code>braveknight</code>來執行的。隻有knights.xml檔案知道哪個騎士執行哪種探險任務。

通過示例我們對依賴注入進行了一個快速介紹。縱覽全書,你将對依賴注入有更多的認識。如果你想了解更多關于依賴注入的資訊,我推薦閱讀dhanji r. prasanna的《dependency injection》,該著作覆寫了依賴注入的所有内容。

現在讓我們再關注spring簡化java開發的下一個理念:基于切面進行聲明式程式設計。

1.1.3 應用切面

di能夠讓互相協作的軟體元件保持松散耦合,而面向切面程式設計(aspect-oriented programming,aop)允許你把遍布應用各處的功能分離出來形成可重用的元件。

面向切面程式設計往往被定義為促使軟體系統實作關注點的分離一項技術。系統由許多不同的元件組成,每一個元件各負責一塊特定功能。除了實作自身核心的功能之外,這些元件還經常承擔着額外的職責。諸如日志、事務管理和安全這樣的系統服務經常融入到自身具有核心業務邏輯的元件中去,這些系統服務通常被稱為橫切關注點,因為它們會跨越系統的多個元件。

如果将這些關注點分散到多個元件中去,你的代碼将會帶來雙重的複雜性。

實作系統關注點功能的代碼将會重複出現在多個元件中。這意味着如果你要改變這些關注點的邏輯,必須修改各個子產品中的相關實作。即使你把這些關注點抽象為一個獨立的子產品,其他子產品隻是調用它的方法,但方法的調用還是會重複出現在各個子產品中。

元件會因為那些與自身核心業務無關的代碼而變得混亂。一個向位址簿增加位址條目的方法應該隻關注如何添加位址,而不應該關注它是不是安全的或者是否需要支援事務。

圖1.2展示了這種複雜性。左邊的業務對象與系統級服務結合得過于緊密。每個對象不但要知道它需要記日志、進行安全控制和參與事務,還要親自執行這些服務。

《Spring實戰(第4版)》——第1章 Spring之旅 1.1簡化Java開發

圖1.2 在整個系統内,關注點(例如日志和安全)

的調用經常散布到各個子產品中,而這些關注點并不是子產品的核心業務

aop能夠使這些服務子產品化,并以聲明的方式将它們應用到它們需要影響的元件中去。所造成的結果就是這些元件會具有更高的内聚性并且會更加關注自身的業務,完全不需要了解涉及系統服務所帶來複雜性。總之,aop能夠確定pojo的簡單性。

如圖1.3所示,我們可以把切面想象為覆寫在很多元件之上的一個外殼。應用是由那些實作各自業務功能的子產品組成的。借助aop,可以使用各種功能層去包裹核心業務層。這些層以聲明的方式靈活地應用到系統中,你的核心應用甚至根本不知道它們的存在。這是一個非常強大的理念,可以将安全、事務和日志關注點與核心業務邏輯相分離。

《Spring實戰(第4版)》——第1章 Spring之旅 1.1簡化Java開發

圖1.3 利用aop,系統範圍内的關注點覆寫在它們所影響元件之上

為了示範在spring中如何應用切面,讓我們重新回到騎士的例子,并為它添加一個切面。

aop應用

每一個人都熟知騎士所做的任何事情,這是因為吟遊詩人用詩歌記載了騎士的事迹并将其進行傳唱。假設我們需要使用吟遊詩人這個服務類來記載騎士的所有事迹。程式清單1.9展示了我們會使用的<code>minstrel</code>類。

程式清單1.9 吟遊詩人是中世紀的音樂記錄器

《Spring實戰(第4版)》——第1章 Spring之旅 1.1簡化Java開發

正如你所看到的那樣,<code>minstrel</code>是隻有兩個方法的簡單類。在騎士執行每一個探險任務之前,<code>singbeforequest()</code>方法會被調用;在騎士完成探險任務之後,<code>singafterquest()</code>方法會被調用。在這兩種情況下,<code>minstrel</code>都會通過一個<code>printstream</code>類來歌頌騎士的事迹,這個類是通過構造器注入進來的。

把<code>minstrel</code>加入你的代碼中并使其運作起來,這對你來說是小事一樁。我們适當做一下調整進而讓<code>braveknight</code>可以使用<code>minstrel</code>。程式清單1.10展示了将<code>braveknight</code>和<code>minstrel</code>組合起來的第一次嘗試。

程式清單1.10 braveknight必須要調用minstrel的方法

《Spring實戰(第4版)》——第1章 Spring之旅 1.1簡化Java開發

這應該可以達到預期效果。現在,你所需要做的就是回到<code>spring</code>配置中,聲明<code>minstrel bean</code>并将其注入到<code>braveknight</code>的構造器之中。但是,請稍等……

我們似乎感覺有些東西不太對。管理他的吟遊詩人真的是騎士職責範圍内的工作嗎?在我看來,吟遊詩人應該做他份内的事,根本不需要騎士指令他這麼做。畢竟,用詩歌記載騎士的探險事迹,這是吟遊詩人的職責。為什麼騎士還需要提醒吟遊詩人去做他份内的事情呢?

此外,因為騎士需要知道吟遊詩人,是以就必須把吟遊詩人注入到<code>barveknight</code>類中。這不僅使<code>braveknight</code>的代碼複雜化了,而且還讓我疑惑是否還需要一個不需要吟遊詩人的騎士呢?如果<code>minstrel</code>為<code>null</code>會發生什麼呢?我是否應該引入一個空值校驗邏輯來覆寫該場景?

簡單的<code>braveknight</code>類開始變得複雜,如果你還需要應對沒有吟遊詩人時的場景,那代碼會變得更複雜。但利用<code>aop</code>,你可以聲明吟遊詩人必須歌頌騎士的探險事迹,而騎士本身并不用直接通路<code>minstrel</code>的方法。

要将<code>minstrel</code>抽象為一個切面,你所需要做的事情就是在一個<code>spring</code>配置檔案中聲明它。程式清單1.11是更新後的<code>knights.xml</code>檔案,minstrel被聲明為一個切面。

程式清單1.11 将minstrel聲明為一個切面

《Spring實戰(第4版)》——第1章 Spring之旅 1.1簡化Java開發

這裡使用了spring的aop配置命名空間把minstrel bean聲明為一個切面。首先,需要把minstrel聲明為一個bean,然後在元素中引用該bean。為了進一步定義切面,聲明(使用)在embarkonquest()方法執行前調用minstrel的singbeforequest()方法。這種方式被稱為前置通知(before advice)。同時聲明(使用)在embarkonquest()方法執行後調用singafter quest()方法。這種方式被稱為後置通知(after advice)。

在這兩種方式中,pointcut-ref屬性都引用了名字為embark的切入點。該切入點是在前邊的元素中定義的,并配置expression屬性來選擇所應用的通知。表達式的文法采用的是aspectj的切點表達式語言。

現在,你無需擔心不了解aspectj或編寫aspectj切點表達式的細節,我們稍後會在第4章詳細地探讨spring aop的内容。現在你已經知道,spring在騎士執行探險任務前後會調用minstrel的singbeforequest()和singafterquest()方法,這就足夠了。

這就是我們需要做的所有的事情!通過少量的xml配置,就可以把minstrel聲明為一個spring切面。如果你現在還沒有完全了解,不必擔心,在第4章你會看到更多的spring aop示例,那将會幫助你徹底弄清楚。現在我們可以從這個示例中獲得兩個重要的觀點。

首先,minstrel仍然是一個pojo,沒有任何代碼表明它要被作為一個切面使用。當我們按照上面那樣進行配置後,在spring的上下文中,minstrel實際上已經變成一個切面了。

其次,也是最重要的,minstrel可以被應用到braveknight中,而braveknight不需要顯式地調用它。實際上,braveknight完全不知道minstrel的存在。

必須還要指出的是,盡管我們使用spring魔法把minstrel轉變為一個切面,但首先要把它聲明為一個spring bean。能夠為其他spring bean做到的事情都可以同樣應用到spring切面中,例如為它們注入依賴。

應用切面來歌頌騎士可能隻是有點好玩而已,但是spring aop可以做很多有實際意義的事情。在後續的各章中,你還會了解基于spring aop實作聲明式事務和安全(第9章和第14章)。

但現在,讓我們再看看 spring簡化java開發的其他方式。

1.1.4 使用模闆消除樣闆式代碼

你是否寫過這樣的代碼,當編寫的時候總會感覺以前曾經這麼寫過?我的朋友,這不是似曾相識。這是樣闆式的代碼(boilerplate code)。通常為了實作通用的和簡單的任務,你不得不一遍遍地重複編寫這樣的代碼。

遺憾的是,它們中的很多是因為使用java api而導緻的樣闆式代碼。樣闆式代碼的一個常見範例是使用jdbc通路資料庫查詢資料。舉個例子,如果你曾經用過jdbc,那麼你或許會寫出類似下面的代碼。

程式清單1.12 許多java api,例如jdbc,會涉及編寫大量的樣闆式代碼

《Spring實戰(第4版)》——第1章 Spring之旅 1.1簡化Java開發

正如你所看到的,這段jdbc代碼查詢資料庫獲得員工姓名和薪水。我打賭你很難把上面的代碼逐行看完,這是因為少量查詢員工的代碼淹沒在一堆jdbc的樣闆式代碼中。首先你需要建立一個資料庫連接配接,然後再建立一個語句對象,最後你才能進行查詢。為了平息jdbc可能會出現的怒火,你必須捕捉sqlexception,這是一個檢查型異常,即使它抛出後你也做不了太多事情。

最後,畢竟該說的也說了,該做的也做了,你不得不清理戰場,關閉資料庫連接配接、語句和結果集。同樣為了平息jdbc可能會出現的怒火,你依然要捕捉sqlexception。

程式清單1.12中的代碼和你實作其他jdbc操作時所寫的代碼幾乎是相同的。隻有少量的代碼與查詢員工邏輯有關系,其他的代碼都是jdbc的樣闆代碼。

jdbc不是産生樣闆式代碼的唯一場景。在許多程式設計場景中往往都會導緻類似的樣闆式代碼,jms、jndi和使用rest服務通常也涉及大量的重複代碼。

spring旨在通過模闆封裝來消除樣闆式代碼。spring的jdbctemplate使得執行資料庫操作時,避免傳統的jdbc樣闆代碼成為了可能。

舉個例子,使用spring的jdbctemplate(利用了 java 5特性的jdbctemplate實作)重寫的getemployeebyid()方法僅僅關注于擷取員工資料的核心邏輯,而不需要迎合jdbc api的需求。程式清單1.13展示了修訂後的getemployeebyid()方法。

程式清單1.13 模闆能夠讓你的代碼關注于自身的職責

《Spring實戰(第4版)》——第1章 Spring之旅 1.1簡化Java開發

正如你所看到的,新版本的getemployeebyid()簡單多了,而且僅僅關注于從資料庫中查詢員工。模闆的queryforobject()方法需要一個sql查詢語句,一個rowmapper對象(把資料映射為一個域對象),零個或多個查詢參數。getemp loyeebyid()方法再也看不到以前的jdbc樣闆式代碼了,它們全部被封裝到了模闆中。

我已經向你展示了spring通過面向pojo程式設計、di、切面和模闆技術來簡化java開發中的複雜性。在這個過程中,我展示了在基于xml的配置檔案中如何配置bean和切面,但這些檔案是如何加載的呢?它們被加載到哪裡去了?讓我們再了解下spring容器,這是應用中的所有bean所駐留的地方。