久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Spring 入門(mén)教程

 玄范 2011-09-08
Spring入門(mén)教程
2010-10-07 20:32

Spring入門(mén)教程
                                     
1.Spring簡(jiǎn)介
(1)Spring是什么
Spring是輕量級(jí)的J2EE應(yīng)用程序開(kāi)源框架,。它由Rod Johnson創(chuàng)建。它是為了解決企業(yè)應(yīng)用開(kāi)發(fā)的復(fù)雜性而創(chuàng)建的,。Spring使用基本的JavaBean來(lái)完成以前只可能由EJB完成的事情,。然而,Spring的用途不僅限于服務(wù)器端的開(kāi)發(fā)。從簡(jiǎn)單性,、可測(cè)試性和松耦合的角度而言,任何Java應(yīng)用都可以從Spring中受益,。
Spring的核心是個(gè)輕量級(jí)容器(container),實(shí)現(xiàn)了IoC(Inversion of Control)模式的容器,Spring的目標(biāo)是實(shí)現(xiàn)一個(gè)全方位的整合框架,,在Spring框架下實(shí)現(xiàn)多個(gè)子框架的組合,,這些子框架之間彼此可以獨(dú)立,,也可以使用其它的框架方案加以替代,,Spring希望提供one-stop shop的框架整合方案
Spring不會(huì)特別去提出一些子框架來(lái)與現(xiàn)有的OpenSource框架競(jìng)爭(zhēng),,除非它覺(jué)得所提出的框架夠新夠好,,例如Spring有自己的 MVC框架方案,因?yàn)樗X(jué)得現(xiàn)有的MVC方案有很多可以改進(jìn)的地方,,但它不強(qiáng)迫您使用它提供的方案,,您可以選用您所希望的框架來(lái)取代其子框架,例如您仍可以在Spring中整合您的Struts框架,。
Spring的核心概念是IoC,,IoC的抽象概念是「依賴(lài)關(guān)系的轉(zhuǎn)移」,,像是「高層模塊不應(yīng)該依賴(lài)低層模塊,,而是模塊都必須依賴(lài)于抽象」是IoC的一種表現(xiàn),「實(shí)現(xiàn)必須依賴(lài)抽象,,而不是抽象依賴(lài)實(shí)現(xiàn)」也是IoC的一種表現(xiàn),「應(yīng)用程序不應(yīng)依賴(lài)于容器,,而是容器服務(wù)于應(yīng)用程序」也是IoC的一種表現(xiàn),。回想一下面向?qū)ο蟮脑O(shè)計(jì)原則:OCP原則和DIP原則,。
Spring的核心即是個(gè)IoC/DI的容器,,它可以幫程序設(shè)計(jì)人員完成組件(類(lèi)別們)之間的依賴(lài)關(guān)系注入(連結(jié)),使得組件(類(lèi)別們)之間的依賴(lài)達(dá)到最小,,進(jìn)而提高組件的重用性,,Spring是個(gè)低侵入性(invasive)的框架,Spring中的組件并不會(huì)意識(shí)到它正置身于Spring中,,這使得組件可以輕易的從框架中脫離,,而幾乎不用任何的修改,反過(guò)來(lái)說(shuō),,組件也可以簡(jiǎn)單的方式加入至框架中,,使得組件甚至框架的整合變得容易。
Spring最為人重視的另一方面是支持AOP(Aspect-Oriented Programming),,然而AOP框架只是Spring支持的一個(gè)子框架,,說(shuō)Spring框架是AOP框架并不是一件適當(dāng)?shù)拿枋觯藗儗?duì)于新奇的 AOP關(guān)注映射至Spring上,,使得人們對(duì)于Spring的關(guān)注集中在它的AOP框架上,,雖然有所誤解,但也突顯了Spring的另一個(gè)令人關(guān)注的特色。
 Spring也提供MVC Web框架的解決方案,,但您也可以將自己所熟悉的MVC Web框架與Spring解合,,像是Struts、Webwork等等,,都可以與Spring整合而成為適用于自己的解決方案,。
 Spring也提供其它方面的整合,像是持久層的整合如JDBC,、O/R Mapping工具(Hibernate,、iBATIS)、事務(wù)處理等等,,Spring作了對(duì)多方面整合的努力,,故說(shuō)Spring是個(gè)全方位的應(yīng)用程序框架。
Spring框架由七個(gè)定義明確的模塊組成:

圖1 Spring框架概覽圖
總的來(lái)說(shuō),,Spring是一個(gè)輕量級(jí)的控制反轉(zhuǎn)(IoC)和面向切面(AOP)的容器框架,。
     輕量??從大小與開(kāi)銷(xiāo)兩方面而言Spring都是輕量的。完整的Spring框架可以在一個(gè)大小只有1MB多的JAR文件里發(fā)布,。并且Spring所需的處理開(kāi)銷(xiāo)也是微不足道的,。此外,Spring是非侵入式的:典型地,,Spring應(yīng)用中的對(duì)象不依賴(lài)于Spring的特定類(lèi)。 
     控制反轉(zhuǎn)??Spring通過(guò)一種稱(chēng)作控制反轉(zhuǎn)(IoC)的技術(shù)促進(jìn)了松耦合,。當(dāng)應(yīng)用了IoC,,一個(gè)對(duì)象依賴(lài)的其它對(duì)象會(huì)通過(guò)被動(dòng)的方式傳遞進(jìn)來(lái),而不是這個(gè)對(duì)象自己創(chuàng)建或者查找依賴(lài)對(duì)象,。你可以認(rèn)為IoC與JNDI相反??不是對(duì)象從容器中查找依賴(lài),,而是容器在對(duì)象初始化時(shí)不等對(duì)象請(qǐng)求就主動(dòng)將依賴(lài)傳遞給它。
     面向切面??Spring提供了面向切面編程的豐富支持,,允許通過(guò)分離應(yīng)用的業(yè)務(wù)邏輯與系統(tǒng)級(jí)服務(wù)(例如審計(jì)(auditing)和事務(wù)()管理)進(jìn)行內(nèi)聚性的開(kāi)發(fā),。應(yīng)用對(duì)象只實(shí)現(xiàn)它們應(yīng)該做的??完成業(yè)務(wù)邏輯??僅此而已。它們并不負(fù)責(zé)(甚至是意識(shí))其它的系統(tǒng)級(jí)關(guān)注點(diǎn),,例如日志或事務(wù)支持,。 
     容器??Spring包含并管理應(yīng)用對(duì)象的配置和生命周期,在這個(gè)意義上它是一種容器,,你可以配置你的每個(gè)bean如何被創(chuàng)建??基于一個(gè)可配置原型(prototype),,你的bean可以創(chuàng)建一個(gè)單獨(dú)的實(shí)例或者每次需要時(shí)都生成一個(gè)新的實(shí)例??以及它們是如何相互關(guān)聯(lián)的。然而,,Spring不應(yīng)該被混同于傳統(tǒng)的重量級(jí)的EJB容器,,它們經(jīng)常是龐大與笨重的,難以使用。 
     框架??Spring可以將簡(jiǎn)單的組件配置,、組合成為復(fù)雜的應(yīng)用,。在Spring中,應(yīng)用對(duì)象被聲明式地組合,,典型地是在一個(gè)XML文件里,。Spring也提供了很多基礎(chǔ)功能(事務(wù)管理、持久化框架集成等等),,將應(yīng)用邏輯的開(kāi)發(fā)留給了你,。 
所有Spring的這些特征使你能夠編寫(xiě)更干凈、更可管理,、并且更易于測(cè)試的代碼,。它們也為Spring中的各種模塊提供了基礎(chǔ)支持。
(2)Spring的歷史
Spring的基礎(chǔ)架構(gòu)起源于2000年早期,,它是Rod Johnson在一些成功的商業(yè)項(xiàng)目中構(gòu)建的基礎(chǔ)設(shè)施,。
在2002后期,Rod Johnson發(fā)布了《Expert One-on-One J2EE Design and Development》一書(shū),,并隨書(shū)提供了一個(gè)初步的開(kāi)發(fā)框架實(shí)現(xiàn)??interface21開(kāi)發(fā)包,,interface21就是書(shū)中闡述的思想的具體實(shí)現(xiàn)。后來(lái),,Rod Johnson 在interface21 開(kāi)發(fā)包的基礎(chǔ)之上,,進(jìn)行了進(jìn)一步的改造和擴(kuò)充,使其發(fā)展為一個(gè)更加開(kāi)放,、清晰,、全面、高效的開(kāi)發(fā)框架??Spring,。
2003年2月Spring框架正式成為一個(gè)開(kāi)源項(xiàng)目,,并發(fā)布于SourceForge中。
(3)Spring的使命
     J2EE應(yīng)該更加容易使用,。
     面向?qū)ο蟮脑O(shè)計(jì)比任何實(shí)現(xiàn)技術(shù)(比如J2EE)都重要,。
     面向接口編程,而不是針對(duì)類(lèi)編程,。Spring將使用接口的復(fù)雜度降低到零,。(面向接口編程有哪些復(fù)雜度?)
     代碼應(yīng)該易于測(cè)試,。Spring框架會(huì)幫助你,,使代碼的測(cè)試更加簡(jiǎn)單。
     JavaBean提供了應(yīng)用程序配置的最好方法,。
     在Java中,,已檢查異常(Checked exception)被過(guò)度使用,。框架不應(yīng)該迫使你捕獲不能恢復(fù)的異常,。

2.控制反轉(zhuǎn)IoC
IoC全名Inversion of Control,,如果中文硬要翻譯過(guò)來(lái)的話(huà),就是「控制反轉(zhuǎn)」,。初看IoC,,從字面上不容易了解其意義,我覺(jué)得要了解IoC,,最好先從Dependency Inversion開(kāi)始了解,,也就是依賴(lài)關(guān)系的反轉(zhuǎn)。
Dependency Inversion在面向?qū)ο蟮脑O(shè)計(jì)原則之依賴(lài)倒置原則(DIP,,Dependence Inversion Principle)中有著清楚的解?,。
簡(jiǎn)單的說(shuō),在模塊設(shè)計(jì)時(shí),,高層的抽象模塊通常是與業(yè)務(wù)相關(guān)的模塊,,它應(yīng)該具有重用性,而不依賴(lài)于低層的實(shí)現(xiàn)模塊,,例如如果低層模塊原先是軟盤(pán)存取模式,,而高層模塊是個(gè)存盤(pán)備份的需求,如果高層模塊直接叫用低層模塊的函式,,則就對(duì)其產(chǎn)生了依賴(lài)關(guān)系,。請(qǐng)看下面的例子:


void Copy(){
   int c;
   while ((c = ReadKeyboard()) != EOF)        
     WritePrinter(c);
}
這是僵化和不易改動(dòng)的例子,為什么呢,?很顯然,,如果我還要將內(nèi)容輸出到磁盤(pán)上(如下圖所示),那么我們必須改動(dòng)Copy的內(nèi)容,,并進(jìn)行重新的測(cè)試和編譯,。


改動(dòng)后的程序如下所示:
enum OutputDevice {printer, disk}; 
void Copy(OutputDevice dev){
   int c;
   while((c = ReadKeyboard())!= EOF)           
      if(dev == printer)
         WritePrinter(c);
      else
         WriteDisk(c);
}
如果要繼續(xù)添加別的輸入或輸出方式,,該程序還是無(wú)法重用,,要對(duì)此程序進(jìn)行修改才能繼續(xù)使用。
利用依賴(lài)倒置原則(DIP),,可以解決這個(gè)問(wèn)題,。DIP原則,可以從2點(diǎn)來(lái)解讀:
     第1點(diǎn):高層模塊不依賴(lài)底層模塊,,兩者都依賴(lài)抽象
     第2點(diǎn):抽象不應(yīng)該依賴(lài)于細(xì)節(jié),,細(xì)節(jié)應(yīng)該依賴(lài)于抽象
上面所講的例子如果用DIP原則,結(jié)果如下

class Reader {
public: 
    virtual int read()=0;
};

class Writer {
public: 
    virtual void write(int)=0;
};

void Copy(Reader& r, Writer& w){
   int c;
   while((c = r.read()) != EOF)
     w.write(c);
}
這樣一來(lái),,如果要添加新的輸入或輸出設(shè)備時(shí)只要改動(dòng)相應(yīng)的類(lèi)(class Reader,Writer,,利用多態(tài)來(lái)解決上面的問(wèn)題)就可以了,而其它的程序都不用改動(dòng)。這就是依賴(lài)倒置原則的基本內(nèi)涵,。
在軟件設(shè)計(jì)和構(gòu)建中我們要遵循“高內(nèi)聚,、低偶合”的原則。那么,,依賴(lài)對(duì)于我們來(lái)說(shuō)究竟是好事還是壞事呢,?
首先應(yīng)該明白的是,類(lèi)之間如果是零偶合的狀態(tài)是不能夠構(gòu)建應(yīng)用程序的,,只能構(gòu)建類(lèi)庫(kù),。
但是由于人類(lèi)的理解力和可控制的范圍有限,大多數(shù)人難以理解和把握過(guò)于復(fù)雜的系統(tǒng),。把軟件系統(tǒng)劃分成多個(gè)模塊,,可以有效控制模塊的復(fù)雜度,使每個(gè)模塊都易于理解和維護(hù),。但在這種情況下,,模塊之間就必須以某種方式交換信息,也就是必然要發(fā)生某種耦合關(guān)系,。如果某個(gè)模塊和其它模塊沒(méi)有任何關(guān)聯(lián)(哪怕只是潛在的或隱含的依賴(lài)關(guān)系),,我們就幾乎可以斷定,該模塊不屬于此軟件系統(tǒng),,應(yīng)該從系統(tǒng)中剔除,。如果所有模塊之間都沒(méi)有任何耦合關(guān)系,其結(jié)果必然是:整個(gè)軟件不過(guò)是多個(gè)互不相干的系統(tǒng)的簡(jiǎn)單堆積,,對(duì)每個(gè)系統(tǒng)而言,,所有功能還是要在一個(gè)模塊中實(shí)現(xiàn),這等于沒(méi)有做任何模塊的分解,。
因此,,模塊之間必定會(huì)有這樣或那樣的依賴(lài)關(guān)系,永遠(yuǎn)不要幻想消除所有依賴(lài),。但是,,過(guò)強(qiáng)的耦合關(guān)系(如一個(gè)模塊的變化會(huì)造成一個(gè)或多個(gè)其他模塊也同時(shí)發(fā)生變化的依賴(lài)關(guān)系)會(huì)對(duì)軟件系統(tǒng)的質(zhì)量造成很大的危害。特別是當(dāng)需求發(fā)生變化時(shí),,代碼的維護(hù)成本將非常高,。所以,我們必須想盡辦法來(lái)控制和消解不必要的耦合,,特別是那種會(huì)導(dǎo)致其它模塊發(fā)生不可控變化的依賴(lài)關(guān)系,。依賴(lài)倒置、控制反轉(zhuǎn)就是人們?cè)诤鸵蕾?lài)關(guān)系進(jìn)行艱苦卓絕的斗爭(zhēng)過(guò)程中不斷產(chǎn)生和發(fā)展起來(lái)的,。
我們下面來(lái)繼續(xù)一步一步的說(shuō)明這個(gè)問(wèn)題,。
看下面的程序:
#include <floppy.h>
....
void save() {
        ....
        saveToFloppy()
    }
}
由于save()程序依賴(lài)于saveToFloppy(),,如果今天要更換低層的存儲(chǔ)模塊為Usb碟,則這個(gè)程序沒(méi)有辦法重用,,必須加以修改才行,,低層模塊的更動(dòng)造成了高層模塊也必須跟著更動(dòng),這不是一個(gè)好的設(shè)計(jì)方式,,我們希望模塊都依賴(lài)于模塊的抽象,,這樣才可以重用高層的業(yè)務(wù)設(shè)計(jì)。
如果以面向?qū)ο蟮姆绞絹?lái)設(shè)計(jì),,依賴(lài)倒置(Dependency Inversion)的解釋變?yōu)槌绦虿粦?yīng)依賴(lài)實(shí)現(xiàn),,而是依賴(lài)于抽象,實(shí)現(xiàn)必須依賴(lài)于抽象,。我們來(lái)看看下面這個(gè)Java程序:
BusinessObject.java
public class BusinessObject {
    private FloppyWriter writer = new FloppyWriter();
    ....
   
    public void save() {
        ...
        writer.saveToFloppy();

    }
}

public class FloppyWriter {
    
    .... //相應(yīng)的寫(xiě)盤(pán)的代碼
   
}
在這個(gè)程序中,,BusinessObject的存盤(pán)依賴(lài)于實(shí)際的FloppyWriter,如果今天我們想要將存盤(pán)改為存至Usb碟,,我們必須修改或繼承BusinessObject進(jìn)行擴(kuò)展,,而無(wú)法直接使用BusinessObject。
浩劫只是剛剛開(kāi)始,,這時(shí),,你一定和我一樣在期待著救世主的早日降臨??接口(Interface)。你一定會(huì)說(shuō),,面向?qū)ο蟮脑O(shè)計(jì)原則已經(jīng)告訴我們了啊,,“要針對(duì)接口編程”,你為什么不用呢,?好,,我們采用這個(gè)原則進(jìn)行編程:
什么是接口?
     接口定義了行為的協(xié)議,,這些行為在繼承接口的類(lèi)中實(shí)現(xiàn),。
     接口定義了很多方法,但是沒(méi)有實(shí)現(xiàn)它們,。類(lèi)履行接口協(xié)議并實(shí)現(xiàn)所有定義在接口中的方法,。
     接口是一種只有聲明沒(méi)有實(shí)現(xiàn)的特殊類(lèi)。
使用接口的優(yōu)點(diǎn):
     Client不必知道其使用對(duì)象的具體所屬類(lèi),。
     一個(gè)對(duì)象可以很容易地被(實(shí)現(xiàn)了相同接口的)的另一個(gè)對(duì)象所替換,。
     對(duì)象間的連接不必硬綁定(hardwire)到一個(gè)具體類(lèi)的對(duì)象上,,因此增加了靈活性,。
     松散藕合(loosens coupling)。
     增加了重用的可能性,。
通過(guò)面向接口編程,,可以改?此一情?,,例如:
public interface IDeviceWriter {
    public void saveToDevice();
}

public class BusinessObject {
    private IDeviceWriter writer;

    public void setDeviceWriter(IDeviceWriter writer) {
        this.writer = writer;
    }

    public void save() {
        ....
        writer.saveToDevice();
    }
}
這樣一來(lái),BusinessObject就是可重用的,,如果今天我有存儲(chǔ)至Floppy或Usb碟的需求,,我只要實(shí)現(xiàn)IDeviceWriter即可,而不用修改BusinessObject:
public class FloppyWriter implement IDeviceWriter {
    public void saveToDevice() {
        ....
        // 實(shí)際儲(chǔ)存至Floppy的程序代碼
    }
}

public class UsbDiskWriter implement IDeviceWriter {
    public void saveToDevice() {
        ....
        // 實(shí)際儲(chǔ)存至UsbDisk的程序代碼
    }
}
從這個(gè)角度來(lái)看,,Dependency Inversion的意思即是程序不依賴(lài)于實(shí)現(xiàn),,而是程序與實(shí)現(xiàn)都要依賴(lài)于抽象。
哦,,這樣一來(lái),,一切都OK了嗎?還沒(méi)有沒(méi)有問(wèn)題呢,,你可能會(huì)有疑問(wèn),,我要根據(jù)不同的情況來(lái)使用軟盤(pán)、USB碟或者其它的存儲(chǔ)設(shè)備,,怎么辦呢,?在程序包中,BusinessObject不是還是和FloppyWriter或者UsbDiskWriter綁定嗎,,如果系統(tǒng)發(fā)布后,,要將FloppyWriter替換為UsbDiskWriter不是還要去修改IDeviceWriter的實(shí)現(xiàn)嗎?修改就意味著可能會(huì)帶來(lái)錯(cuò)誤,,就要帶來(lái)修改代碼,、進(jìn)行測(cè)試、編譯,、維護(hù)等的工作量,。還有更好的方法嗎?IoC/DI就是解決之道,。
IoC的Control是控制的意思,,其實(shí)其背后的意義也是一種依賴(lài)關(guān)系的轉(zhuǎn)移,如果A依賴(lài)于B,,其意義即是B擁有控制權(quán),,我們要轉(zhuǎn)移這種關(guān)系,所以依賴(lài)關(guān)系的反轉(zhuǎn)即是控制關(guān)系的反轉(zhuǎn),,藉由控制關(guān)系的轉(zhuǎn)移,,我們可以獲得組件的可重用性,在上面的Java程序中,,整個(gè)控制權(quán)從實(shí)際的 FloppyWriter轉(zhuǎn)移至抽象的IDeviceWriter接口上,,使得BusinessObject、FloppyWriter,、 UsbDiskWriter這幾個(gè)實(shí)現(xiàn)依賴(lài)于抽象的IDeviceWriter接口,。
使用IoC,,對(duì)象是被動(dòng)的接受依賴(lài)類(lèi),而不是自己主動(dòng)的去找,。容器在實(shí)例化的時(shí)候主動(dòng)將它的依賴(lài)類(lèi)注入給它,。可以這樣理解:控制反轉(zhuǎn)將類(lèi)的主動(dòng)權(quán)轉(zhuǎn)移到接口上,,依賴(lài)注入通過(guò)xml配置文件在類(lèi)實(shí)例化時(shí)將其依賴(lài)類(lèi)注入,。
使用IoC,對(duì)象是被動(dòng)的接受依賴(lài)類(lèi),,而不是自己主動(dòng)的去找,。容器在實(shí)例化的時(shí)候主動(dòng)將它的依賴(lài)類(lèi)注入給它??梢赃@樣理解:控制反轉(zhuǎn)將類(lèi)的主動(dòng)權(quán)轉(zhuǎn)移到接口上,,依賴(lài)注入通過(guò)xml配置文件在類(lèi)實(shí)例化時(shí)將其依賴(lài)類(lèi)注入。
“控制反轉(zhuǎn)(Inversion of Control)與依賴(lài)倒置原則(Dependency Inversion Principle)是一個(gè)同義原則,。雖然“依賴(lài)倒置”和“控制反轉(zhuǎn)”在設(shè)計(jì)層面上都是消解模塊耦合的有效方法,,也都是試圖令具體的、易變的模塊依賴(lài)于抽象的,、穩(wěn)定的模塊的基本原則,,但二者在使用語(yǔ)境和關(guān)注點(diǎn)上存在差異:“依賴(lài)倒置”強(qiáng)調(diào)的是對(duì)于傳統(tǒng)的、源于面向過(guò)程設(shè)計(jì)思想的層次概念的“倒置”,,而“控制反轉(zhuǎn)”強(qiáng)調(diào)的是對(duì)程序流程控制權(quán)的反轉(zhuǎn),。“依賴(lài)倒置”的使用范圍更為寬泛一些,。
IoC在容器的角度,,可以用這么一句好萊塢名言來(lái)代表(著名的好萊塢原則):"Don't call me, I'll call you."(不要打電話(huà)給我們,我們會(huì)通知你的),。好萊塢的演員門(mén)(包括大牌)都會(huì)在好萊塢進(jìn)行登記,,他們不能夠直接打電話(huà)給制片人或者導(dǎo)演要求演出摸個(gè)角色或者參加某個(gè)片子的演出,而是由好萊塢根據(jù)需要去通知他們(前提是他們已經(jīng)在好萊塢做過(guò)登記),。在這里好萊塢就相當(dāng)于容器,。
以程序的術(shù)語(yǔ)來(lái)說(shuō)的話(huà),就是「不要向容器要求您所需要的(對(duì)象)資源,,容器會(huì)自動(dòng)將這些對(duì)象給您,!」。IoC要求的是容器不侵入應(yīng)用程序本身,,應(yīng)用程序本身提供好接口,,容器可以透過(guò)這些接口將所需的資源注至程序中,應(yīng)用程序不向容器主動(dòng)要求資源,故而不會(huì)依賴(lài)于容器的組件,,應(yīng)用程序本身不會(huì)意識(shí)到正被容器使用,可以隨時(shí)從容器中脫離轉(zhuǎn)移而不用作任何的修改,,而這個(gè)特性正是一些業(yè)務(wù)邏輯中間件最需要的,。

3.依?注入DI
IoC模式基本上是一個(gè)高層的概念,在Martin Fowler的Inversion of Control Containers and the Dependency Injection pattern中談到,,實(shí)現(xiàn)IoC有兩種方式:Dependency Injection與Service Locator,。
Spring所采用的是Dependency Injection來(lái)實(shí)現(xiàn)IoC(多數(shù)容器都是采取這種方式的),中文翻譯為依賴(lài)注入,,依賴(lài)注入的意義是:保留抽象接口,,讓組件依賴(lài)于抽象接口,當(dāng)組件要與其它實(shí)際的對(duì)象發(fā)生依賴(lài)關(guān)系時(shí),,藉過(guò)抽象接口來(lái)注入依賴(lài)的實(shí)際對(duì)象,。
回鍋頭來(lái)再仔細(xì)研讀一下我們?cè)谏厦娼o出的例子:
public interface IDeviceWriter {
    public void saveToDevice();
}

public class BusinessObject {
    private IDeviceWriter writer;

    public void setDeviceWriter(IDeviceWriter writer) {
        this.writer = writer;
    }

    public void save() {
        ....
        writer.saveToDevice();
    }
}

public class FloppyWriter implement IDeviceWriter {
    public void saveToDevice() {
        ....
        // ???存至Floppy的程式?
    }
}

public class UsbDiskWriter implement IDeviceWriter {
    public void saveToDevice() {
        ....
        // ???存至UsbDisk的程式?
    }
}
為了讓BusinessObject獲得重用性,我們不讓BusinessObject依賴(lài)于實(shí)際的FloppyWriter,,而是依賴(lài)于抽象的接口,。
在此代碼中,首先IDeviceWriter的變量writer可以接收任何IDeviceWriter的實(shí)例,,另外,,F(xiàn)loppyWriter或UsbDiskWrite的實(shí)例不是通過(guò)BusinessObject來(lái)獲得,而是通過(guò)setter(也可以用構(gòu)造器)來(lái)由外部傳給它,。這似乎跟我們往常的代碼沒(méi)什么不同(回想一下Javabean的setter/getter),,但這已經(jīng)是一個(gè)良好的設(shè)計(jì)。現(xiàn)在的關(guān)鍵問(wèn)題是:FloppyWriter或UsbDiskWrite的實(shí)例如何從外部注入給BusinessObject呢,?這就要通過(guò)xml來(lái)實(shí)現(xiàn)了(相當(dāng)于演員們?cè)诤萌R塢登記),。
Spring的配置文件applicationContext.xml代碼片斷如下:
<beans>
    <bean id = "FloppyWriter" class = "iocfirst.business.write" />
    <bean
        id = "BusinessObject"
        class = "iocfirst.business.BusinessObject"
    />
        <property name = " FloppyWriter">
            <ref bean = " FloppyWriter " />
        </property>
    </bean>
</beans>
如果什么時(shí)候想將UsbDiskWrite注入,則修改applicationContext.xm即可,。
單有了這個(gè)xml文件還不夠,,加載該xml文件呢?spring提供了現(xiàn)成的API,,在加載上面的xml的時(shí)候,,就進(jìn)行了如下工作:實(shí)例化FloppyWriter或UsbDiskWrite類(lèi),實(shí)例化BusinessObject類(lèi),,并將FloppyWriter或UsbDiskWrite的實(shí)例作為參數(shù)賦給了BusinessObject實(shí)例的setDeviceWriter方法,。
BusinessObject依賴(lài)于抽象接口,在需要建立依賴(lài)關(guān)系時(shí),,我們就是通過(guò)抽象接口注入依賴(lài)的實(shí)際對(duì)象,。
依賴(lài)注入在Martin Fowler的文章中談到了三種實(shí)現(xiàn)方式:interface injection、setter injection與constructor injection,。并分別稱(chēng)其為type 1 IoC,、type 2 IoC與type 3 IoC,。
     Type1-接口注入(Interface Injection)
它是在一個(gè)接口中定義需要注入的信息,并通過(guò)接口完成注入,。Apache Avalon是一個(gè)較為典型的Type1型IOC容器,,WebWork框架的IoC容器也是Type1型。
     Type2-設(shè)值方法注入(Setter Injection)
在各種類(lèi)型的依賴(lài)注入模式中,,設(shè)值注入模式在實(shí)際開(kāi)發(fā)中得到了最廣泛的應(yīng)用(其中很大一部分得力于Spring框架的影響),。
基于設(shè)置模式的依賴(lài)注入機(jī)制更加直觀、也更加自然,。上面的BusinessObject所實(shí)現(xiàn)的是type 2 IoC,,透過(guò)setter注入依賴(lài)關(guān)系。
     Type3-構(gòu)造子注入(Constructor Injection)
構(gòu)造子注入,,即通過(guò)構(gòu)造函數(shù)完成依賴(lài)關(guān)系的設(shè)定,。
目前我們只要了解到有這個(gè)3種方式就可以了,具體情況將在以后的章節(jié)中進(jìn)行介紹,。

4.開(kāi)始spring之旅
任何需要交給spring管理的對(duì)象,,都必須在配置文件中注冊(cè),這個(gè)過(guò)程被稱(chēng)為wiring,,下面做一個(gè)最簡(jiǎn)單的演示,,
第一步:下載Spring,Spring官方網(wǎng)站是http://www./我們這里下載的是2.0.1版,,spring-framework-2.0.1-with-dependencies.zip是該版本的壓縮包,,參見(jiàn)圖2。

                  圖 2
將壓縮包的內(nèi)容解壓到一個(gè)目錄中,,D:\spring-framework-2.0.1是我這里使用的存放目錄,。
第二步:使用Eclipse的【new project】向?qū)В陆ㄒ粋€(gè)名為spring-demo的工程,。參見(jiàn)圖3,。

                           圖3

第三步:新建一個(gè)名為HelloTalker的類(lèi),package選擇com.spring.demo,,如“圖4”所示,。

                圖 4
第四步:把所需要使用的Spring項(xiàng)目jar的文件加入到當(dāng)前工程的Libraries中,本例中只使用Spring的簡(jiǎn)單IOC功能,,只需要spring-beans.jar,、spring-core.jar以及commons-logging.jar三個(gè)包即可。
右鍵點(diǎn)擊工程名稱(chēng),,查看工程屬性,,選擇工程屬性中的【Java Builder Path】,然后選擇【Libraries】選項(xiàng),通過(guò)點(diǎn)擊【add external jar】按鈕來(lái)把外部的jar文件添加到工程項(xiàng)目中,。參見(jiàn)圖5所示:

                  圖 5
第五步,,錄入HelloTalker.java的完整源代碼。其中HelloTalker這個(gè)類(lèi)有一個(gè)msg屬性,,為該屬性編寫(xiě)set方法,,該方法必須嚴(yán)格遵守javabean的命名規(guī)則即有一個(gè)setMsg方法用于設(shè)置msg屬性的值。另外,,我們直接在HelloTalker的main方法中使用Spring的Bean工廠來(lái)從配置文件中加載一個(gè)名為helloBean,,類(lèi)型為com.spring.demo.HelloTalker的Bean,。然后調(diào)用這個(gè)Bean的sayHello方法,。全部源代碼如下所示:
package com.spring.demo;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

public class HelloTalker {

    /**
     * @param args
     */
    private String msg;
    public void setMsg(String msg)
    {
        this.msg=msg;
    }
    public void sayHello()
    {
        System.out.print(msg);
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Resource res=new ClassPathResource("com/spring/demo/springConfig.xml");
        BeanFactory factory=new XmlBeanFactory(res);
        HelloTalker hello=(HelloTalker)factory.getBean("helloBean");
        hello.sayHello();
}
}

第六步,任何需要交給spring管理的對(duì)象,,都必須在配置文件中注冊(cè),,這個(gè)過(guò)程被稱(chēng)為wiring。因此,,在與HelloTalker.java同級(jí)的目錄下,,建一個(gè)名為springConfig.xml(文件名可以任意定)的Spring配置文件,在這個(gè)Bean配置文件中定義helloBean,,并通過(guò)依賴(lài)注入設(shè)置helloBean的msg屬性值,,其內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www./dtd/spring-beans.dtd">
<beans>
   <bean id="helloBean" class="com.spring.demo.HelloTalker">
       <property name="msg" value="Hello World的Spring示例!"/>
   </bean>   
</beans>
參見(jiàn)圖6:

圖6,工程完成后的情況
第七步:運(yùn)行程序,,在HelloTalker.java上點(diǎn)右鍵,,選擇【Run As】下面的【Java Application】。即可運(yùn)行這個(gè)使用了Spring的程序,,該程序?qū)⑤敵鰄elloBean中的msg屬性的值:“Hwllo World的Spring示例!”,,如圖7所示。

圖7 程序運(yùn)行的結(jié)果
至此為止,,我們就完成了一個(gè)最簡(jiǎn)單的Spring應(yīng)用實(shí)踐,。更多的內(nèi)容將在后面的部分逐步給出。

4.注入方式
前面已經(jīng)提到依賴(lài)注入方式由種,,Spring鼓勵(lì)的是setter injection,,但也允許您使用constructor injection,使用setter或constructor來(lái)注入依賴(lài)關(guān)系視您的需求而定,,使用constructor的好處之一是,,您可以在建構(gòu)對(duì)象的同時(shí)一并完成依賴(lài)關(guān)系的建立,然而如果要建立的對(duì)象關(guān)系很多,,則會(huì)在建構(gòu)函式上留下一長(zhǎng)串的參數(shù),,這時(shí)使用setter會(huì)是個(gè)不錯(cuò)的選擇,另一方面, setter可以有明確的名稱(chēng)可以了解注入的對(duì)象會(huì)是什么,,像是setXXX()這樣的名稱(chēng)會(huì)比記憶constructor上某個(gè)參數(shù)位置代表某個(gè)對(duì)象來(lái)得好,。
前面講的例子采用的是setter injection。Type 1 IoC是interface injection,,使用type 1 IoC時(shí)會(huì)要求實(shí)現(xiàn)接口,,這個(gè)接口是為容器所用的,容器知道接口上所規(guī)定的方法,,它可以呼叫實(shí)現(xiàn)接口的對(duì)象來(lái)完成依賴(lài)關(guān)系的注入,,例如:
public interface IDependencyInjection {
    public void createDependency(Map dependObjects);
}

public class BusinessObject implement IDependencyInjection {
    private Map dependObjects;

    public void createDependency(Map dependObjects) {
        this.dependObject = dependObjects;
        // 在這邊實(shí)現(xiàn)與BusinessObject的依賴(lài)關(guān)系
        ......
    }

    public void save() {
        ....
        writer.saveToDevice();
    }
}

如果要完成依賴(lài)關(guān)系注入的對(duì)象,必須實(shí)現(xiàn)IDependencyInjection接口,,并交由容器管理,,容器會(huì)呼叫被管理對(duì)象的createDependency()方法來(lái)完成依賴(lài)關(guān)系的建立。
在上面的例子中,,type 1 IoC要求BusinessObject實(shí)現(xiàn)特定的接口,,這就使得BusinessObject依賴(lài)于容器,如果日后BusinessObject要脫離目前這個(gè)容器,,就必須修改程序,,想想在更復(fù)雜的依賴(lài)關(guān)系中產(chǎn)生更多復(fù)雜的接口,組件與容器(框架)的依賴(lài)會(huì)更加復(fù)雜,,最后使得組件無(wú)法從容器中脫離,。
所以type 1 IoC具有強(qiáng)的侵入性,使用它來(lái)實(shí)現(xiàn)依賴(lài)注入會(huì)使得組件相依于容器(框架),,降低組件的重用性,。

Spring的核心是個(gè)IoC容器,您可以用setter或constructor的方式來(lái)實(shí)現(xiàn)您的業(yè)務(wù)對(duì)象,,至于對(duì)象與對(duì)象之間的關(guān)系建立,,則透過(guò)組態(tài)設(shè)定,讓Spring在執(zhí)行時(shí)期根據(jù)組態(tài)檔的設(shè)定來(lái)為您建立對(duì)象之間的依賴(lài)關(guān)系,。
那么,,如何使用construtor injection呢?首先看看HelloBean:
HelloBean.java
package onlyfun.caterpillar;

public class HelloBean {
    private String helloWord = "hello";
    private String user = "NoBody";
   
    public HelloBean(String helloWord, String user) {
        this.helloWord = helloWord;
        this.user = user;
    }   
   
    public String sayHelloToUser() {
        return helloWord + "!" + user + "!";
    }
}
為了突顯構(gòu)造函式,,我們這個(gè)HelloBean設(shè)計(jì)的簡(jiǎn)陋,,只提供了構(gòu)造函式與必要的sayHelloToUser(),我們來(lái)看看bean的定義檔案:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www./dtd/spring-beans.dtd">
<beans>
    <bean id="helloBean" class="onlyfun.caterpillar.HelloBean">
        <constructor-arg index="0"><value>Greeting</value></constructor-arg>
        <constructor-arg index="1"><value>Justin</value></constructor-arg>
    </bean>
</beans>
在bean的定義檔案中,,我們使用<constructor-arg>來(lái)表示我們將使用constructor injection,,由于使用constructor injection時(shí)并不如setter injection時(shí)擁有setXXX()這樣易懂的名稱(chēng),所以我們必須指定參數(shù)的位置索引,,index屬性就是用于指定我們的對(duì)象將注入至構(gòu)造函式中的哪一個(gè)參數(shù),,索引值從0開(kāi)始,,符合Java索引的慣例。
來(lái)看看測(cè)試程序:
Test.java
package onlyfun.caterpillar;

import java.io.*;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;

public class Test {
    public static void main(String[] args) throws IOException {
        InputStream is = new FileInputStream("bean.xml");
        BeanFactory factory = new XmlBeanFactory(is);
       
        HelloBean hello = (HelloBean) factory.getBean("helloBean");
        System.out.println(hello.sayHelloToUser());
    }
}

幾種依賴(lài)注入模式的特點(diǎn)如下:
接口注入模式因?yàn)闅v史較為悠久,,在很多容器中都已經(jīng)得到應(yīng)用,。但由于其在靈活性、易用性上不如
其他兩種注入模式,,因而在IOC的專(zhuān)題世界內(nèi)并不被看好,。
Type2和Type3型的依賴(lài)注入實(shí)現(xiàn)則是目前主流的IOC實(shí)現(xiàn)模式。這兩種實(shí)現(xiàn)方式各有特點(diǎn),,也各具優(yōu)勢(shì),。
Type2 設(shè)值注入的優(yōu)勢(shì)
1. 對(duì)于習(xí)慣了傳統(tǒng)JavaBean開(kāi)發(fā)的程序員而言,通過(guò)setter方法設(shè)定依賴(lài)關(guān)系顯得更加直觀,,更加自然,。
2. 如果依賴(lài)關(guān)系(或繼承關(guān)系)較為復(fù)雜,那么Type3模式的構(gòu)造函數(shù)也會(huì)相當(dāng)龐大(我們需要在構(gòu)造函數(shù)中設(shè)定所有依賴(lài)關(guān)系),,此時(shí)Type2模式往往更為簡(jiǎn)潔,。
3. 對(duì)于某些第三方類(lèi)庫(kù)而言,,可能要求我們的組件必須提供一個(gè)默認(rèn)的構(gòu)造函數(shù)(如Struts中的Action),,此時(shí)Type3類(lèi)型的依賴(lài)注入機(jī)制就體現(xiàn)出其局限性,難以完成我們期望的功能,。
Type3 構(gòu)造子注入的優(yōu)勢(shì):
1. “在構(gòu)造期即創(chuàng)建一個(gè)完整,、合法的對(duì)象”,對(duì)于這條Java設(shè)計(jì)原則,,Type3無(wú)疑是最好的響應(yīng)者,。
2. 避免了繁瑣的setter方法的編寫(xiě),所有依賴(lài)關(guān)系均在構(gòu)造函數(shù)中設(shè)定,,依賴(lài)關(guān)系集中呈現(xiàn),,更加易讀。
3. 由于沒(méi)有setter方法,,依賴(lài)關(guān)系在構(gòu)造時(shí)由容器一次性設(shè)定,,因此組件在被創(chuàng)建之后即處于相對(duì)“不變”的穩(wěn)定狀態(tài),無(wú)需擔(dān)心上層代碼在調(diào)用過(guò)程中執(zhí)行setter方法對(duì)組件依賴(lài)關(guān)系產(chǎn)生破壞,,特別是對(duì)于Singleton模式的組件而言,,這可能對(duì)整個(gè)系統(tǒng)產(chǎn)生重大的影響。
4. 同樣,,由于關(guān)聯(lián)關(guān)系僅在構(gòu)造函數(shù)中表達(dá),,只有組件創(chuàng)建者需要關(guān)心組件內(nèi)部的依賴(lài)關(guān)系。對(duì)調(diào)用者而言,,組件中的依賴(lài)關(guān)系處于黑盒之中,。對(duì)上層屏蔽不必要的信息,,也為系統(tǒng)的層次清晰性提供了保證。
5. 通過(guò)構(gòu)造子注入,,意味著我們可以在構(gòu)造函數(shù)中決定依賴(lài)關(guān)系的注入順序,,對(duì)于一個(gè)大量依賴(lài)外部服務(wù)的組件而言,依賴(lài)關(guān)系的獲得順序可能非常重要,,比如某個(gè)依賴(lài)關(guān)系注入的先決條件是組件的UserDao及相關(guān)資源已經(jīng)被設(shè)定,。
可見(jiàn),Type3和Type2模式各有千秋,,而Spring,、PicoContainer都對(duì)Type3和Type2類(lèi)型的依賴(lài)注入機(jī)制提供了良好支持。這也就為我們提供了更多的選擇余地,。理論上,,以Type3類(lèi)型為主,輔之以Type2類(lèi)型機(jī)制作為補(bǔ)充,,可以達(dá)到最好的依賴(lài)注入效果,,不過(guò)對(duì)于基于Spring Framework開(kāi)發(fā)的應(yīng)用而言,Type2使用更加廣泛,。

本文來(lái)自CSDN博客,,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/rommal7090/archive/2009/03/28/4032266.aspx

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多