1. 程式人生 > >Spring框架 IOC(原理詳解)(一)

Spring框架 IOC(原理詳解)(一)

一、Spring開源框架的簡介

Spring是一個開源框架,Spring是於2003 年興起的一個輕量級的Java 開發框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中闡述的部分理念和原型衍生而來。它是為了解決企業應用開發的複雜性而建立的。Spring使用基本的JavaBean來完成以前只可能由EJB完成的事情。然而,Spring的用途不僅限於伺服器端的開發。從簡單性、可測試性和鬆耦合的角度而言,任何Java應用都可以從Spring中受益。 簡單來說,Spring是一個輕量級的控制反轉(IoC)和麵向切面(AOP)的容器框架。

  spring的基本框架主要包含六大模組:DAO、ORM、AOP、JEE、WEB、CORE
  這裡寫圖片描述
Spring DAO:Spring 提供了對JDBC的操作支援:JdbcTemplate模板工具類。
Spring ORM:Spring 可以與ORM框架整合,例如Spring整合Hibernate框架,其中Spring 還提供HibernateDaoSupport工具類,簡化Hibernate的操作
Spring WEB:Spring 提供了對Struts、Springmvc的支援,支援WEB開發。此時同時Spring自身也提供了基於MVC的解決方案
Spring AOP:Spring提供了面向切面程式設計,可以給某一層提供事物管理,例如在Service層新增事物控制。
Spring JEE

:J2EE開發規範的支援,例如EJB
Spring Core:提供IOC容器物件的建立和處理依賴關係

二、Spring下IOC容器

IOC容器:就是具有依賴注入功能的容器,是可以建立物件的容器,IOC容器負責例項化、定位、配置應用程式中的物件及建立這些物件間的依賴。通常new一個例項,控制權由程式設計師控制,而”控制反轉”是指new例項工作不由程式設計師來做而是交給Spring容器來做。。在Spring中BeanFactory是IOC容器的實際代表者
下面通過例項理解IOC概念

場景:電影墨攻中:當劉德華所飾演的墨者革離到達樑國都城下,城上樑國守軍問道:‘城下和人’,劉德華答到:“墨者隔離”;
下面通過一個java類為這個城門對話進行編劇,藉此理解IOC概念

程式碼一:MoAttack:通過演員安排劇本

public class MoAttack{
    public void cityGateAsk(){
        //演員直接入侵劇本
        LiuDeHua  ldh = new LiuDeHua();
        ldh.responseAsk("墨者革離");
    }
}

上面發現,作為具體角色飾演者的劉德華直接侵入劇本中,使劇本和演員直接耦合在一起。

這裡寫圖片描述

但是一個明智的編劇創作時圍繞故事的角色進行,而不是角色的具體飾演者,這樣就可以自由的選取合適的演員。通過上面分析,我們需要為該劇本主人隔離定義一個介面:在影片開拍時,導演將LiuDeHua安排在GeLi的角色上,導演將劇本、角色、飾演者裝配起來
這裡寫圖片描述
通過引入導演,使得劇本和具體飾演者解耦了,具體軟體中 導演相當於裝配器,安排演員表演具體的角色。

如果此時還是對ioc不理解 下面我摘抄一篇部落格博主的通俗理解,感覺說的很好:

簡單理解IOC
例子1:
我們從文章中發現,其實ioc所做的一件事情就是把A和B的強耦合關係,變成A依賴於B的介面的關係,但具體A要實現B介面中哪一種B型別,由C來決定,以達到解耦,通俗來講,我們在家到飯點的時候就會說“我要吃飯”,我這裡代表的是A,飯代表的是B的介面,但是具體是要吃什麼飯,那就由你的媽媽在決定,你媽媽給你在碗裡放了米飯(B),你就要吃米飯,當然,今天你媽媽開心,也可以給你碗裡放一個雞腿,這個決定權在你的媽媽,也就是我們常說的把控制權交給第三方。一次來達到我(A)和米飯(B)的解耦。
例子2:
IoC與DI
首先想說說IoC(Inversion of Control,控制倒轉)。這是spring的核心,貫穿始終。所謂IoC,對於spring框架來說,就是由spring來負責控制物件的生命週期和物件間的關係。這是什麼意思呢,舉個簡單的例子,我們是如何找女朋友的?常見的情況是,我們到處去看哪裡有長得漂亮身材又好的mm,然後打聽她們的興趣愛好、qq號、電話號、ip號、iq號………,想辦法認識她們,投其所好送其所要,然後嘿嘿……這個過程是複雜深奧的,我們必須自己設計和麵對每個環節。傳統的程式開發也是如此,在一個物件中,如果要使用另外的物件,就必須得到它(自己new一個,或者從JNDI中查詢一個),使用完之後還要將物件銷燬(比如Connection等),物件始終會和其他的介面或類藕合起來。

  那麼IoC是如何做的呢?有點像通過婚介找女朋友,在我和女朋友之間引入了一個第三者:婚姻介紹所。婚介管理了很多男男女女的資料,我可以向婚介提出一個列表,告訴它我想找個什麼樣的女朋友,比如長得像李嘉欣,身材像林熙雷,唱歌像周杰倫,速度像卡洛斯,技術像齊達內之類的,然後婚介就會按照我們的要求,提供一個mm,我們只需要去和她談戀愛、結婚就行了。簡單明瞭,如果婚介給我們的人選不符合要求,我們就會丟擲異常。整個過程不再由我自己控制,而是有婚介這樣一個類似容器的機構來控制。Spring所倡導的開發方式就是如此,所有的類都會在spring容器中登記,告訴spring你是個什麼東西,你需要什麼東西,然後spring會在系統執行到適當的時候,把你要的東西主動給你,同時也把你交給其他需要你的東西。所有的類的建立、銷燬都由 spring來控制,也就是說控制物件生存週期的不再是引用它的物件,而是spring。對於某個具體的物件而言,以前是它控制其他物件,現在是所有物件都被spring控制,所以這叫控制反轉。如果你還不明白的話,我決定放棄。

IoC的一個重點是在系統執行中,動態的向某個物件提供它所需要的其他物件。這一點是通過DI(Dependency Injection,依賴注入)來實現的。比如物件A需要操作資料庫,以前我們總是要在A中自己編寫程式碼來獲得一個Connection物件,有了 spring我們就只需要告訴spring,A中需要一個Connection,至於這個Connection怎麼構造,何時構造,A不需要知道。在系統執行時,spring會在適當的時候製造一個Connection,然後像打針一樣,注射到A當中,這樣就完成了對各個物件之間關係的控制。A需要依賴 Connection才能正常執行,而這個Connection是由spring注入到A中的,依賴注入的名字就這麼來的。那麼DI是如何實現的呢? Java 1.3之後一個重要特徵是反射(reflection),它允許程式在執行的時候動態的生成物件、執行物件的方法、改變物件的屬性,spring就是通過反射來實現注入的。關於反射的相關資料請查閱java doc。
 理解了IoC和DI的概念後,一切都將變得簡單明瞭,剩下的工作只是在spring的框架中堆積木而已。

如果還不明白,放棄java吧!

DI與IOC 的關係
我們可能會經常聽到另一個詞:DI,這裡,簡單的做一下講解:
因為IOC確實不夠開門見山,因此業界曾進行了廣泛的討論,最終軟體界的泰斗級人物MartinFowIer提出了DI(依注入:Dependency Injection)的概念用以代替loc,即讓呼叫類對某一介面實現類的依賴關係由第三方(容器或協作類)注入,以移除呼叫類對某一介面實現類的依賴。“依賴注入”這個名詞顯然比“控制反轉”直接明瞭、易於理解。
所以,我認為IOC和DI描述的是一件事情,只是從不同的角度來描述:
IOC控制反轉:說的是建立物件例項的控制權從程式碼控制剝離到IOC容器控制,實際上就是我們現在說的第三方,側重於原理。
DI依賴注入:說的是建立物件例項時,為這個物件注入屬性值或其它物件例項,側重於實現。
說到DI,可能就會有出現這樣一個問題,既然DI側重實現,那麼他是怎麼實現的呢?也就是怎麼注入的呢?

IOC的型別
1、建構函式注入
MoAttact:通過建構函式注入革離扮演者

public class MoAttact{
    private GeLi geli;
    //注入革離的具體扮演者
    public MoAttact(GeLi geli){
        this.geli = geli;
    }
    public void cityGateAsk(){
        geli.response("墨者革離");
    }
}

角色的具體扮演由導演來安排

Director: 通過建構函式注入革離扮演者

public class Director{
    public void direct(){
        //指定角色的扮演者
        GeLi geli =  new LiuDeHua();
        //注入具體扮演者到劇本中
        MoAttack moAttack = new MoAttack(geli);
        moAttack.cityGateAsk();
    }
}

2、屬性注入
有時候會發現,革離雖然是主演,但是不是任何時候都需要他的出現,在這種情況下建構函式注入是不妥的,這個時候可以考慮屬性注入
Mottack:通過setter方法注入革離扮演者

public class MoAttact{
    private GeLi geli;
    //屬性注入
    public void setGeli(GeLi geli){
        this.geli = geli;
    }
    public void cityGateAsk(){
        geli.response("墨者革離");
    }
}

讓導演在需要時候注入

public class Director{
    public void direct(){
        //指定角色的扮演者
        GeLi geli =  new LiuDeHua();
        //注入具體扮演者到劇本中
        MoAttack moAttack = new MoAttack();
        moAttack .setGeLi(geli);
        moAttack.cityGateAsk();
    }
}

3、介面注入

將呼叫類的依賴注入方法抽取到一個介面中,呼叫實現介面類方法

public interface ActorAraagable{
    void injectGeli(GeLi geli)
}

實現此類

public class MoAttact implement  ActorAraagable{
    private GeLi geli;
    //實現介面注入
    public void injectGeli(GeLi geli){
        this.geli = geli;
    }
    public void cityGateAsk(){
        geli.response("墨者革離");
    }
}

然後通過導演呼叫實現類

public class Director{
    public void direct(){
        //指定角色的扮演者
        GeLi geli =  new LiuDeHua();
        //注入具體扮演者到劇本中
        MoAttack moAttack = new MoAttack();
        moAttack .injectGeli(geli);
        moAttack.cityGateAsk();
    }
}

三、通過容器完成依賴關係的注入

以上三種注入方式,雖然MoAttack和LiuDeHua實現瞭解耦,MoAttack無需關注角色實現類的例項化工作,但是這些工作在程式碼中依然存在,只是轉移到了導演Director類中,假設某一製片人想改邊局面,在選擇某個劇本的時候,希望通過海選或者第三方機構來選擇導演,演員,讓他們各司其職,這樣 導演、演員都實現瞭解耦。

第三方機構幫助完成類的初始化與裝配工作,讓開發者從底層實現類的例項化,依賴關係裝配等工作中脫離出來,專注於業務的實現。

//例項化類
<bean id="geli" class="LiuDeHua"/>
<bean id="moAttack" class="com.examples.moAttack"
      p:geli-ref="geli">//通過geli-ref建立依賴關係
</bean>

通過new XmlBeanFactory(bean.xml)等方式啟動容器,在容器啟動時,Spring根據配置檔案描述的資訊自動化例項bean並實現裝配,後面可以直接使用。