《Spring實戰(第4版)》——第1章 Spring之旅 1.1簡化Java開發
本節書摘來自異步社區《Spring實戰(第4版)》一書中的第1章,第1.1節,作者: 【美】Craig Walls(沃爾斯)著,更多章節內容可以訪問雲棲社區“異步社區”公眾號查看
第1部分 Spring的核心
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用於聲明式安全以及緩存。
第1章 Spring之旅
本章內容:
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所解決的各類問題有一個清晰的認識,同時為其他章奠定基礎。
1.1 簡化Java開發
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上有任何不合理的要求
可以看到,這是一個簡單普通的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探險任務
可以看到,DamselRescuingKnight
在它的構造函數中自行創建了Rescue DamselQuest
。這使得DamselRescuingKnight
緊密地和RescueDamselQuest
耦合到了一起,因此極大地限製了這個騎士執行探險的能力。如果一個少女需要救援,這個騎士能夠召之即來。但是如果一條惡龍需要殺掉,或者一個圓桌……額……需要滾起來,那麼這個騎士就愛莫能助了。
更糟糕的是,為這個DamselRescuingKnight
編寫單元測試將出奇地困難。在這樣的一個測試中,你必須保證當騎士的embarkOnQuest()
方法被調用的時候,探險的embark()方法也要被調用。但是沒有一個簡單明了的方式能夠實現這一點。很遺憾,DamselRescuingKnight
將無法進行測試。
耦合具有兩麵性(two-headed beast)。一方麵,緊密耦合的代碼難以測試、難以複用、難以理解,並且典型地表現出“打地鼠”式的bug特性(修複一個bug,將會出現一個或者更多新的bug)。另一方麵,一定程度的耦合又是必須的——完全沒有耦合的代碼什麼也做不了。為了完成有實際意義的功能,不同的類必須以適當的方式進行交互。總而言之,耦合是必須的,但應當被小心謹慎地管理。
通過DI,對象的依賴關係將由係統中負責協調各對象的第三方組件在創建對象的時候進行設定。對象無需自行創建或管理它們的依賴關係,如圖1.1所示,依賴關係將被自動注入到需要它們的對象當中去。
圖1.1 依賴注入會將所依賴的關係自動交給目標對象,而不是讓對象自己去獲取依賴
為了展示這一點,讓我們看一看程序清單1.3中的BraveKnight
,這個騎士不僅勇敢,而且能挑戰任何形式的探險。
程序清單1.3 BraveKnight足夠靈活可以接受任何賦予他的探險任務
我們可以看到,不同於之前的DamselRescuingKnight
,BraveKnight
沒有自行創建探險任務,而是在構造的時候把探險任務作為構造器參數傳入。這是依賴注入的方式之一,即構造器注入(constructor injection)。
更重要的是,傳入的探險類型是Quest,也就是所有探險任務都必須實現的一個接口。所以,BraveKnight
能夠響應RescueDamselQuest
、 SlayDragonQuest
、 MakeRoundTableRounderQuest
等任意的Quest實現。
這裏的要點是BraveKnight
沒有與任何特定的Quest實現發生耦合。對它來說,被要求挑戰的探險任務隻要實現了Quest接口,那麼具體是哪種類型的探險就無關緊要了。這就是DI所帶來的最大收益——鬆耦合。如果一個對象隻通過接口(而不是具體實現或初始化過程)來表明依賴關係,那麼這種依賴就能夠在對象本身毫不知情的情況下,用不同的具體實現進行替換。
對依賴進行替換的一個最常用方法就是在測試的時候使用mock
實現。我們無法充分地測試DamselRescuingKnight
,因為它是緊耦合的;但是可以輕鬆地測試BraveKnigh
t,隻需給它一個Quest
的mock
實現即可,如程序清單1.4所示。
程序清單1.4 為了測試BraveKnight,需要注入一個mock Quest
你可以使用mock框架Mockito去創建一個Quest
接口的mock實現。通過這個mock對象,就可以創建一個新的BraveKnight
實例,並通過構造器注入這個mock Quest
。當調用embarkOnQuest()
方法時,你可以要求Mockito
框架驗證Quest
的mock實現的embark()
方法僅僅被調用了一次。
將Quest注入到Knight中
現在BraveKnight
類可以接受你傳遞給它的任意一種Quest
的實現,但該怎樣把特定的Quest
實現傳給它呢?假設,希望BraveKnight
所要進行探險任務是殺死一隻怪龍,那麼程序清單1.5中的SlayDragonQuest
也許是挺合適的。
程序清單1.5 SlayDragonQuest是要注入到BraveKnight中的Quest實現
我們可以看到,SlayDragonQuest
實現了Quest
接口,這樣它就適合注入到BraveKnight
中去了。與其他的Java入門樣例有所不同,SlayDragonQuest
沒有使用System.out.println()
,而是在構造方法中請求一個更為通用的PrintStream
。這裏最大的問題在於,我們該如何將SlayDragonQuest
交給BraveKnight
呢?又如何將PrintStream
交給SlayDragonQuest
呢?
創建應用組件之間協作的行為通常稱為裝配(wiring)。Spring有多種裝配bean的方式,采用XML是很常見的一種裝配方式。程序清單1.6展現了一個簡單的Spring配置文件:knights.xml,該配置文件將BraveKnight
、SlayDragonQuest
和PrintStream
裝配到了一起。
程序清單1.6 使用Spring將SlayDragonQuest注入到BraveKnight中
在這裏,BraveKnight
和SlayDragonQuest
被聲明為Spring中的bean。就BraveKnight bean
來講,它在構造時傳入了對SlayDragonQuest bean
的引用,將其作為構造器參數。同時,SlayDragonQuest bean
的聲明使用了Spring表達式語言(Spring Expression Language),將System.out
(這是一個PrintStream
)傳入到了SlayDragonQuest
的構造器中。
如果XML配置不符合你的喜好的話,Spring還支持使用Java來描述配置。比如,程序清單1.7展現了基於Java的配置,它的功能與程序清單1.6相同。
程序清單1.7 Spring提供了基於Java的配置,可作為XML的替代方案
不管你使用的是基於XML的配置還是基於Java的配置,DI所帶來的收益都是相同的。盡管BraveKnight
依賴於Quest
,但是它並不知道傳遞給它的是什麼類型的Quest
,也不知道這個Quest
來自哪裏。與之類似,SlayDragonQuest
依賴於PrintStream
,但是在編碼時它並不需要知道這個`PrintStream
是什麼樣子的。隻有Spring通過它的配置,能夠了解這些組成部分是如何裝配起來的。這樣的話,就可以在不改變所依賴的類的情況下,修改依賴關係。
這個樣例展現了在Spring中裝配bean的一種簡單方法。謹記現在不要過多關注細節。第2章我們會深入講解Spring的配置文件,同時還會了解Spring裝配bean的其他方式,甚至包括一種讓Spring自動發現bean並在這些bean之間建立關聯關係的方式。
現在已經聲明了BraveKnight
和Quest
的關係,接下來我們隻需要裝載XML配置文件,並把應用啟動起來。
觀察它如何工作
Spring通過應用上下文(Application Context)裝載bean的定義並把它們組裝起來。Spring應用上下文全權負責對象的創建和組裝。Spring自帶了多種應用上下文的實現,它們之間主要的區別僅僅在於如何加載配置。
因為knights.xml中的bean是使用XML文件進行配置的,所以選擇ClassPathXmlApplicationContext
[1]作為應用上下文相對是比較合適的。該類加載位於應用程序類路徑下的一個或多個XML配置文件。程序清單1.8中的main()
方法調用ClassPathXmlApplicationContext
加載knights.xml,並獲得Knight
對象的引用。
程序清單1.8 KnightMain.java加載包含Knight的Spring上下文
這裏的main()
方法基於knights.xml文件創建了Spring應用上下文。隨後它調用該應用上下文獲取一個ID為knight的bean。得到Knight
對象的引用後,隻需簡單調用embarkOnQuest()
方法就可以執行所賦予的探險任務了。注意這個類完全不知道我們的英雄騎士接受哪種探險任務,而且完全沒有意識到這是由BraveKnight
來執行的。隻有knights.xml文件知道哪個騎士執行哪種探險任務。
通過示例我們對依賴注入進行了一個快速介紹。縱覽全書,你將對依賴注入有更多的認識。如果你想了解更多關於依賴注入的信息,我推薦閱讀Dhanji R. Prasanna的《Dependency Injection》,該著作覆蓋了依賴注入的所有內容。
現在讓我們再關注Spring簡化Java開發的下一個理念:基於切麵進行聲明式編程。
1.1.3 應用切麵
DI能夠讓相互協作的軟件組件保持鬆散耦合,而麵向切麵編程(aspect-oriented programming,AOP)允許你把遍布應用各處的功能分離出來形成可重用的組件。
麵向切麵編程往往被定義為促使軟件係統實現關注點的分離一項技術。係統由許多不同的組件組成,每一個組件各負責一塊特定功能。除了實現自身核心的功能之外,這些組件還經常承擔著額外的職責。諸如日誌、事務管理和安全這樣的係統服務經常融入到自身具有核心業務邏輯的組件中去,這些係統服務通常被稱為橫切關注點,因為它們會跨越係統的多個組件。
如果將這些關注點分散到多個組件中去,你的代碼將會帶來雙重的複雜性。
實現係統關注點功能的代碼將會重複出現在多個組件中。這意味著如果你要改變這些關注點的邏輯,必須修改各個模塊中的相關實現。即使你把這些關注點抽象為一個獨立的模塊,其他模塊隻是調用它的方法,但方法的調用還是會重複出現在各個模塊中。
組件會因為那些與自身核心業務無關的代碼而變得混亂。一個向地址簿增加地址條目的方法應該隻關注如何添加地址,而不應該關注它是不是安全的或者是否需要支持事務。
圖1.2展示了這種複雜性。左邊的業務對象與係統級服務結合得過於緊密。每個對象不但要知道它需要記日誌、進行安全控製和參與事務,還要親自執行這些服務。
圖1.2 在整個係統內,關注點(例如日誌和安全)
的調用經常散布到各個模塊中,而這些關注點並不是模塊的核心業務
AOP能夠使這些服務模塊化,並以聲明的方式將它們應用到它們需要影響的組件中去。所造成的結果就是這些組件會具有更高的內聚性並且會更加關注自身的業務,完全不需要了解涉及係統服務所帶來複雜性。總之,AOP能夠確保POJO的簡單性。
如圖1.3所示,我們可以把切麵想象為覆蓋在很多組件之上的一個外殼。應用是由那些實現各自業務功能的模塊組成的。借助AOP,可以使用各種功能層去包裹核心業務層。這些層以聲明的方式靈活地應用到係統中,你的核心應用甚至根本不知道它們的存在。這是一個非常強大的理念,可以將安全、事務和日誌關注點與核心業務邏輯相分離。
圖1.3 利用AOP,係統範圍內的關注點覆蓋在它們所影響組件之上
為了示範在Spring中如何應用切麵,讓我們重新回到騎士的例子,並為它添加一個切麵。
AOP應用
每一個人都熟知騎士所做的任何事情,這是因為吟遊詩人用詩歌記載了騎士的事跡並將其進行傳唱。假設我們需要使用吟遊詩人這個服務類來記載騎士的所有事跡。程序清單1.9展示了我們會使用的Minstrel
類。
程序清單1.9 吟遊詩人是中世紀的音樂記錄器
正如你所看到的那樣,Minstrel
是隻有兩個方法的簡單類。在騎士執行每一個探險任務之前,singBeforeQuest()
方法會被調用;在騎士完成探險任務之後,singAfterQuest()
方法會被調用。在這兩種情況下,Minstrel
都會通過一個PrintStream
類來歌頌騎士的事跡,這個類是通過構造器注入進來的。
把Minstrel
加入你的代碼中並使其運行起來,這對你來說是小事一樁。我們適當做一下調整從而讓BraveKnight
可以使用Minstrel
。程序清單1.10展示了將BraveKnight
和Minstrel
組合起來的第一次嚐試。
程序清單1.10 BraveKnight必須要調用Minstrel的方法
這應該可以達到預期效果。現在,你所需要做的就是回到Spring
配置中,聲明Minstrel bean
並將其注入到BraveKnight
的構造器之中。但是,請稍等……
我們似乎感覺有些東西不太對。管理他的吟遊詩人真的是騎士職責範圍內的工作嗎?在我看來,吟遊詩人應該做他份內的事,根本不需要騎士命令他這麼做。畢竟,用詩歌記載騎士的探險事跡,這是吟遊詩人的職責。為什麼騎士還需要提醒吟遊詩人去做他份內的事情呢?
此外,因為騎士需要知道吟遊詩人,所以就必須把吟遊詩人注入到BarveKnight
類中。這不僅使BraveKnight
的代碼複雜化了,而且還讓我疑惑是否還需要一個不需要吟遊詩人的騎士呢?如果Minstrel
為null
會發生什麼呢?我是否應該引入一個空值校驗邏輯來覆蓋該場景?
簡單的BraveKnight
類開始變得複雜,如果你還需要應對沒有吟遊詩人時的場景,那代碼會變得更複雜。但利用AOP
,你可以聲明吟遊詩人必須歌頌騎士的探險事跡,而騎士本身並不用直接訪問Minstrel
的方法。
要將Minstrel
抽象為一個切麵,你所需要做的事情就是在一個Spring
配置文件中聲明它。程序清單1.11是更新後的knights.xml
文件,Minstrel被聲明為一個切麵。
程序清單1.11 將Minstrel聲明為一個切麵
這裏使用了Spring的aop配置命名空間把Minstrel bean聲明為一個切麵。首先,需要把Minstrel聲明為一個bean,然後在aop:aspect元素中引用該bean。為了進一步定義切麵,聲明(使用aop:before)在embarkOnQuest()方法執行前調用Minstrel的singBeforeQuest()方法。這種方式被稱為前置通知(before advice)。同時聲明(使用aop:after)在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,會涉及編寫大量的樣板式代碼
正如你所看到的,這段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 模板能夠讓你的代碼關注於自身的職責
正如你所看到的,新版本的getEmployeeById()簡單多了,而且僅僅關注於從數據庫中查詢員工。模板的queryForObject()方法需要一個SQL查詢語句,一個RowMapper對象(把數據映射為一個域對象),零個或多個查詢參數。GetEmp loyeeById()方法再也看不到以前的JDBC樣板式代碼了,它們全部被封裝到了模板中。
我已經向你展示了Spring通過麵向POJO編程、DI、切麵和模板技術來簡化Java開發中的複雜性。在這個過程中,我展示了在基於XML的配置文件中如何配置bean和切麵,但這些文件是如何加載的呢?它們被加載到哪裏去了?讓我們再了解下Spring容器,這是應用中的所有bean所駐留的地方。
最後更新:2017-05-31 10:01:25