Spring的使命
Spring是一個(gè)開源框架问顷,最早由Rod Johnson創(chuàng)建噪奄,并在《Expert Oneon-One:J2EE Design and Development》這本著作中進(jìn)行了介紹。
Spring最根本的使命是解決企業(yè)級(jí)應(yīng)用開發(fā)的復(fù)雜性,即簡(jiǎn)化Java開發(fā)。
Spring可以做很多事情,它為企業(yè)級(jí)開發(fā)提供給了豐富的功能酝锅,但是這些功能的底層都依賴于它的兩個(gè)核心特性,也就是依賴注入(dependency injection奢方,DI)和面向切面編程(aspect-oriented programming搔扁,AOP)。
為了降低Java開發(fā)的復(fù)雜性袱巨,Spring采取了以下4種關(guān)鍵策略:
- 基于POJO的輕量級(jí)和最小侵入性編程阁谆;
- 通過依賴注入和面向接口實(shí)現(xiàn)松耦合;
- 基于切面和慣例進(jìn)行聲明式編程愉老;
- 通過切面和模板減少樣板式代碼场绿。
激發(fā)POJO的潛能
很多框架通過強(qiáng)迫應(yīng)用繼承它們的類或?qū)崿F(xiàn)它們的接口從而導(dǎo)致應(yīng)用與框架綁死。一個(gè)典型的例子是EJB 2時(shí)代的無狀態(tài)會(huì)話bean嫉入。早期的EJB是一個(gè)很容易想到的例子焰盗,不過這種侵入式的編程方式在早期版本的Struts、WebWork咒林、Tapestry以及無數(shù)其他的Java規(guī)范和框架中都能看到熬拒。
Spring竭力避免因自身的API而弄亂你的應(yīng)用代碼。Spring不會(huì)強(qiáng)迫你實(shí)現(xiàn)Spring規(guī)范的接口或繼承Spring規(guī)范的類垫竞,相反澎粟,在基于Spring構(gòu)建的應(yīng)用中,它的類通常沒有任何痕跡表明你使用了Spring欢瞪。最壞的場(chǎng)景是活烙,一個(gè)類或許會(huì)使用Spring注解,但它依舊是POJO遣鼓。
Spring的非侵入編程模型意味著這個(gè)類在Spring應(yīng)用和非Spring應(yīng)用中都可以發(fā)揮同樣的作用啸盏。
依賴注入
耦合具有兩面性(two-headed beast)。一方面骑祟,緊密耦合的代碼難以測(cè)試回懦、難以復(fù)用气笙、難以理解,并且典型地表現(xiàn)出“打地鼠”式的bug特性(修復(fù)一個(gè)bug怯晕,將會(huì)出現(xiàn)一個(gè)或者更多新的bug)潜圃。另一方面,一定程度的耦合又是必須的——完全沒有耦合的代碼什么也做不了舟茶。為了完成有實(shí)際意義的功能秉犹,不同的類必須以適當(dāng)?shù)姆绞竭M(jìn)行交互≈赏恚總而言之,耦合是必須的型诚,但應(yīng)當(dāng)被小心謹(jǐn)慎地管理客燕。
通過DI,對(duì)象的依賴關(guān)系將由系統(tǒng)中負(fù)責(zé)協(xié)調(diào)各對(duì)象的第三方組件在創(chuàng)建對(duì)象的時(shí)候進(jìn)行設(shè)定狰贯。對(duì)象無需自行創(chuàng)建或管理它們的依賴關(guān)系也搓,如下圖所示,依賴關(guān)系將被自動(dòng)注入到需要它們的對(duì)象當(dāng)中去涵紊。
依賴注入會(huì)將所依賴的關(guān)系自動(dòng)交給目標(biāo)對(duì)象傍妒,而不是讓對(duì)象自己去獲取依賴。
創(chuàng)建應(yīng)用組件之間協(xié)作的行為通常稱為裝配(wiring)摸柄。Spring有多種裝配bean的方式颤练,采用XML是很常見的一種裝配方式。如果XML配置不符合你的喜好的話驱负,Spring還支持使用Java來描述配置嗦玖。
Spring通過應(yīng)用上下文(Application Context)裝載bean的定義并把它們組裝起來。Spring應(yīng)用上下文全權(quán)負(fù)責(zé)對(duì)象的創(chuàng)建和組裝跃脊。Spring自帶了多種應(yīng)用上下文的實(shí)現(xiàn)宇挫,它們之間主要的區(qū)別僅僅在于如何加載配置。
面向切面編程
DI能夠讓相互協(xié)作的軟件組件保持松散耦合酪术,而面向切面編程(aspect-oriented programming器瘪,AOP)允許你把遍布應(yīng)用各處的功能分離出來形成可重用的組件。
面向切面編程往往被定義為促使軟件系統(tǒng)實(shí)現(xiàn)關(guān)注點(diǎn)的分離一項(xiàng)技術(shù)绘雁。系統(tǒng)由許多不同的組件組成橡疼,每一個(gè)組件各負(fù)責(zé)一塊特定功能。除了實(shí)現(xiàn)自身核心的功能之外咧七,這些組件還經(jīng)常承擔(dān)著額外的職責(zé)衰齐。諸如日志、事務(wù)管理和安全這樣的系統(tǒng)服務(wù)經(jīng)常融入到自身具有核心業(yè)務(wù)邏輯的組件中去继阻,這些系統(tǒng)服務(wù)通常被稱為橫切關(guān)注點(diǎn)耻涛,因?yàn)樗鼈儠?huì)跨越系統(tǒng)的多個(gè)組件废酷。
如果將這些關(guān)注點(diǎn)分散到多個(gè)組件中去,你的代碼將會(huì)帶來雙重的復(fù)雜性抹缕。
- 實(shí)現(xiàn)系統(tǒng)關(guān)注點(diǎn)功能的代碼將會(huì)重復(fù)出現(xiàn)在多個(gè)組件中澈蟆。這意味著如果你要改變這些關(guān)注點(diǎn)的邏輯,必須修改各個(gè)模塊中的相關(guān)實(shí)現(xiàn)卓研。即使你把這些關(guān)注點(diǎn)抽象為一個(gè)獨(dú)立的模塊趴俘,其他模塊只是調(diào)用它的方法,但方法的調(diào)用還是會(huì)重復(fù)出現(xiàn)在各個(gè)模塊中奏赘。
- 組件會(huì)因?yàn)槟切┡c自身核心業(yè)務(wù)無關(guān)的代碼而變得混亂寥闪。一個(gè)向地址簿增加地址條目的方法應(yīng)該只關(guān)注如何添加地址,而不應(yīng)該關(guān)注它是不是安全的或者是否需要支持事務(wù)磨淌。
在整個(gè)系統(tǒng)內(nèi)疲憋,關(guān)注點(diǎn)(例如日志和安全)的調(diào)用經(jīng)常散布到各個(gè)模塊中,而這些關(guān)注點(diǎn)并不是模塊的核心業(yè)務(wù)梁只。
AOP能夠使這些服務(wù)模塊化缚柳,并以聲明的方式將它們應(yīng)用到它們需要影響的組件中去。所造成的結(jié)果就是這些組件會(huì)具有更高的內(nèi)聚性并且會(huì)更加關(guān)注自身的業(yè)務(wù)搪锣,完全不需要了解涉及系統(tǒng)服務(wù)所帶來復(fù)雜性秋忙。總之构舟,AOP能夠確保POJO的簡(jiǎn)單性灰追。
如下圖所示,我們可以把切面想象為覆蓋在很多組件之上的一個(gè)外殼狗超。應(yīng)用是由那些實(shí)現(xiàn)各自業(yè)務(wù)功能的模塊組成的监嗜。借助AOP,可以使用各種功能層去包裹核心業(yè)務(wù)層抡谐。這些層以聲明的方式靈活地應(yīng)用到系統(tǒng)中裁奇,你的核心應(yīng)用甚至根本不知道它們的存在。這是一個(gè)非常強(qiáng)大的理念麦撵,可以將安全刽肠、事務(wù)和日志關(guān)注點(diǎn)與核心業(yè)務(wù)邏輯相分
離。
使用模板消除樣板式代碼
你是否寫過這樣的代碼免胃,當(dāng)編寫的時(shí)候總會(huì)感覺以前曾經(jīng)這么寫過音五?我的朋友,這不是似曾相識(shí)羔沙。這是樣板式的代碼(boilerplate code)躺涝。通常為了實(shí)現(xiàn)通用的和簡(jiǎn)單的任務(wù),你不得不一遍遍地重復(fù)編寫這樣的代碼扼雏。
遺憾的是坚嗜,它們中的很多是因?yàn)槭褂肑ava API而導(dǎo)致的樣板式代碼夯膀。樣板式代碼的一個(gè)常見范例是使用JDBC訪問數(shù)據(jù)庫(kù)查詢數(shù)據(jù)。
Spring旨在通過模板封裝來消除樣板式代碼苍蔬。
容納你的Bean
在基于Spring的應(yīng)用中诱建,你的應(yīng)用對(duì)象生存于Spring容器(container)中。Spring容器負(fù)責(zé)創(chuàng)建對(duì)象碟绑,裝配它們俺猿,配置它們并管理它們的整個(gè)生命周期,從生存到死亡(在這里格仲,可能就是new到finalize())押袍。
容器是Spring框架的核心。Spring容器使用DI管理構(gòu)成應(yīng)用的組件凯肋,它會(huì)創(chuàng)建相互協(xié)作的組件之間的關(guān)聯(lián)伯病。毫無疑問,這些對(duì)象更簡(jiǎn)單干凈否过,更易于理解,更易于重用并且更易于進(jìn)行單元測(cè)試惭蟋。
Spring容器并不是只有一個(gè)苗桂。Spring自帶了多個(gè)容器實(shí)現(xiàn),可以歸為兩種不同的類型告组。bean工廠(由org.springframework. beans.factory.eanFactory接口定義)是最簡(jiǎn)單的容器煤伟,提供基本的DI支持。應(yīng)用上下文
(由org.springframework.context.ApplicationContext接口定義)基于BeanFactory構(gòu)建木缝,并提供應(yīng)用框架級(jí)別的服務(wù)便锨,例如從屬性文件解析文本信息以及發(fā)布應(yīng)用事件給感興趣的事件監(jiān)聽者。
雖然我們可以在bean工廠和應(yīng)用上下文之間任選一種我碟,但bean工廠對(duì)大多數(shù)應(yīng)用來說往往太低級(jí)了放案,因此,應(yīng)用上下文要比bean工廠更受歡迎矫俺。我們會(huì)把精力集中在應(yīng)用上下文的使用上吱殉,不再浪費(fèi)時(shí)間討論bean工廠。
bean的生命周期
在傳統(tǒng)的Java應(yīng)用中厘托,bean的生命周期很簡(jiǎn)單友雳。使用Java關(guān)鍵字new進(jìn)行bean實(shí)例化,然后該bean就可以使用了铅匹。一旦該bean不再被使用押赊,則由Java自動(dòng)進(jìn)行垃圾回收。相比之下包斑,Spring容器中的bean的生命周期就顯得相對(duì)復(fù)雜多了流礁。正確理解Spring bean的生命周期非常重要涕俗,因?yàn)槟慊蛟S要利用Spring提供的擴(kuò)展點(diǎn)來自定義bean的創(chuàng)建過程。下圖展示了bean裝載到Spring應(yīng)用上下文中的一個(gè)典型的生命周期過程崇棠。
bean在Spring容器中從創(chuàng)建到銷毀經(jīng)歷了若干階段咽袜,每一階段都可以針對(duì)Spring如何管理bean進(jìn)行個(gè)性化定制。
正如你所見枕稀,在bean準(zhǔn)備就緒之前询刹,bean工廠執(zhí)行了若干啟動(dòng)步驟。
我們對(duì)圖1.5進(jìn)行詳細(xì)描述:
1.Spring對(duì)bean進(jìn)行實(shí)例化萎坷;
2.Spring將值和bean的引用注入到bean對(duì)應(yīng)的屬性中凹联;
3.如果bean實(shí)現(xiàn)了BeanNameAware接口,Spring將bean的ID傳遞給setBean-Name()方法哆档;
4.如果bean實(shí)現(xiàn)了BeanFactoryAware接口蔽挠,Spring將調(diào)用setBeanFactory()方法,將BeanFactory容器實(shí)例傳入瓜浸;
5.如果bean實(shí)現(xiàn)了ApplicationContextAware接口澳淑,Spring將調(diào)用setApplicationContext()方法,將bean所在的應(yīng)用上下文的引用傳入進(jìn)來插佛;
6.如果bean實(shí)現(xiàn)了BeanPostProcessor接口杠巡,Spring將調(diào)用它們的post-ProcessBeforeInitialization()方法;
7.如果bean實(shí)現(xiàn)了InitializingBean接口雇寇,Spring將調(diào)用它們的after-PropertiesSet()方法氢拥。類似地,如果bean使用initmethod聲明了初始化方法锨侯,該方法也會(huì)被調(diào)用嫩海;
8.如果bean實(shí)現(xiàn)了BeanPostProcessor接口,Spring將調(diào)用它們的post-ProcessAfterInitialization()方法囚痴;
9.此時(shí)叁怪,bean已經(jīng)準(zhǔn)備就緒,可以被應(yīng)用程序使用了深滚,它們將一直駐留在應(yīng)用上下文中骂束,直到該應(yīng)用上下文被銷毀;
10.如果bean實(shí)現(xiàn)了DisposableBean接口成箫,Spring將調(diào)用它的destroy()接口方法展箱。同樣,如果bean使用destroy-method聲明了銷毀方法蹬昌,該方法也會(huì)被調(diào)用混驰。
現(xiàn)在你已經(jīng)了解了如何創(chuàng)建和加載一個(gè)Spring容器。但是一個(gè)空的容器并沒有太大的價(jià)值,在你把東西放進(jìn)去之前栖榨,它里面什么都沒有昆汹。為了從Spring的DI中受益,我們必須將應(yīng)用對(duì)象裝配進(jìn)Spring容器中婴栽。
Spring模塊
小結(jié)
現(xiàn)在满粗,你應(yīng)該對(duì)Spring的功能特性有了一個(gè)清晰的認(rèn)識(shí)。Spring致力于簡(jiǎn)化企業(yè)級(jí)Java開發(fā)愚争,促進(jìn)代碼的松散耦合映皆。成功的關(guān)鍵在于依賴注入和AOP。
在本章轰枝,我們先體驗(yàn)了Spring的DI捅彻。DI是組裝應(yīng)用對(duì)象的一種方式,借助這種方式對(duì)象無需知道依賴來自何處或者依賴的實(shí)現(xiàn)方式鞍陨。不同于自己獲取依賴對(duì)象步淹,對(duì)象會(huì)在運(yùn)行期賦予它們所依賴的對(duì)象。依賴對(duì)象通常會(huì)通過接口調(diào)用所注入的對(duì)象诚撵,這樣的話就能確保低耦合缭裆。
除了DI,我們還簡(jiǎn)單介紹了Spring對(duì)AOP的支持寿烟。AOP可以幫助應(yīng)用將散落在各處的邏輯匯集于一處——切面澈驼。當(dāng)Spring裝配bean的時(shí)候,這些切面能夠在運(yùn)行期編織起來韧衣,這樣就能非常有效地賦予bean新的行為。
依賴注入和AOP是Spring框架最核心的部分购桑,因此只有理解了如何應(yīng)用Spring最關(guān)鍵的功能畅铭,你才有能力使用Spring框架的其他功能。