理解 Spring IOC

依賴注入是一種實(shí)現(xiàn),而 IOC 是一種設(shè)計(jì)思想。

一、IOC是什么?

IOC—Inversion of Control,即“控制反轉(zhuǎn)”,不是什么技術(shù),而是一種設(shè)計(jì)思想。在Java開發(fā)中,IOC意味著將你設(shè)計(jì)好的對象交給容器控制,而不是傳統(tǒng)的在你的對象內(nèi)部直接控制。

誰控制誰,控制什么:傳統(tǒng)Java SE程序設(shè)計(jì),我們直接在對象內(nèi)部通過new進(jìn)行創(chuàng)建對象,是程序主動去創(chuàng)建依賴對象;而IOC是有專門一個容器來創(chuàng)建這些對象,即由IOC容器來控制對象的創(chuàng)建。

解讀:
提到控制就要理解控制的含義,控制就是對象的創(chuàng)建、初始化、銷毀。

  1. 創(chuàng)建對象,原來是 new 一個,現(xiàn)在給 Spring 容器創(chuàng)建了。
  2. 對象初始化,比如 A 依賴 B,原來是通過構(gòu)造器或者 setter 方法賦值,現(xiàn)在給 Spring 容器自動注入了。
  3. 銷毀對象,原來是直接賦值 null 或者做一些銷毀操作,現(xiàn)在給 Spring 容器管理生命周期負(fù)責(zé)銷毀。

總之,IOC 解決了繁瑣的對象生命周期的操作,解耦了我們的代碼。

為何是反轉(zhuǎn),哪些方面反轉(zhuǎn)了:有反轉(zhuǎn)就有正轉(zhuǎn),傳統(tǒng)應(yīng)用程序是由我們自己在對象中主動控制去直接獲取依賴對象,也就是正轉(zhuǎn);而反轉(zhuǎn)則是由容器來幫忙創(chuàng)建及注入依賴對象。為何是反轉(zhuǎn)?因?yàn)橛扇萜鞑檎壹白⑷胍蕾噷ο?,對象只是被動的接受依賴對象,所以是反轉(zhuǎn)。哪些方面反轉(zhuǎn)了?依賴對象的獲取被反轉(zhuǎn)了。

解讀:
反轉(zhuǎn)的是什么?反轉(zhuǎn)的是控制權(quán)。前面提到 Spring 控制了對象的生命周期,那么對象的控制就完全脫離了我們的控制,交給了 Spring。這個反轉(zhuǎn)是指,我們由對象的控制者變成了 IOC 的被動接受者。我們無法決定對象生命周期的任何一個階段,最多是借助于 Spring 的擴(kuò)展機(jī)制做一些微小的動作,我們甚至無法預(yù)判依賴的對象真正被注入的是哪一個。反轉(zhuǎn)好比你家的機(jī)器人決定了你的生活節(jié)奏,必須聽他的。

二、IOC能做什么?

IOC 不是一種技術(shù),只是一種思想,一個重要的面向?qū)ο缶幊痰姆▌t,它能指導(dǎo)我們設(shè)計(jì)出松耦合、更優(yōu)良的程序。把創(chuàng)建和查找依賴對象的控制權(quán)交給容器,由容器進(jìn)行注入組合對象,所以對象與對象之間是松散耦合,利于功能復(fù)用,更重要的是使得程序的整個體系結(jié)構(gòu)變得非常靈活。

解讀:
借助 IOC 容器完美解決了耦合問題,甚至可以讓八竿子打不著的類型產(chǎn)生注入關(guān)系。比如二方包 a 里面有個類型 A1,三方包 b 里面有個類型 B1,這兩個都是 readOnly 的,你不可能去創(chuàng)建或修改內(nèi)部的依賴。但是借助于 IOC,可以把我們自己的類 C1 注入進(jìn)去,讓 A1、B1 依賴于 C1,從而改變二方包的行為。這也是大型企業(yè)級開發(fā)中 IOC 比較常見的用法。

其實(shí)IOC對編程帶來的最大改變不是從代碼上,而是從思想上發(fā)生了“主從換位”的變化。應(yīng)用程序原本是老大,要獲取什么資源都是主動出擊,但是在IOC/DI思想中,應(yīng)用程序就變成被動的了,被動的等待IOC容器來創(chuàng)建并注入它所需要的資源了。

解讀:
在 IOC 模式下,設(shè)計(jì)流程變成了真正的無關(guān)業(yè)務(wù)完全獨(dú)立的流程化設(shè)計(jì)。你只需要設(shè)計(jì)良好的流程和依賴,定義出需要什么,然后把控制權(quán)交給 Spring 即可。Spring 給你什么對象,你就用什么對象,不要對對象做任何的假設(shè),也不要期待對象有什么特性,只需要等待 Spring 提供對象即可。控制反轉(zhuǎn)就像喬布斯的理念,我告訴你你需要什么。

三、IOC和DI

DI—Dependency Injection,即“依賴注入”:組件之間依賴關(guān)系由容器在運(yùn)行期決定,形象的說,即由容器動態(tài)的將某個依賴關(guān)系注入到組件之中。依賴注入的目的并非為軟件系統(tǒng)帶來更多功能,而是為了提升組件重用的頻率,并為系統(tǒng)搭建一個靈活、可擴(kuò)展的平臺。通過依賴注入機(jī)制,我們只需要通過簡單的配置,而無需任何代碼就可指定目標(biāo)需要的資源,完成自身的業(yè)務(wù)邏輯,而不需要關(guān)心具體的資源來自何處,由誰實(shí)現(xiàn)。

解讀:
依賴注入是一種實(shí)現(xiàn),而 IOC 是一種設(shè)計(jì)思想。從 IOC 到 DI,就是從理論到了實(shí)踐。你把依賴交給了容器,容器幫你管理依賴,這就是依賴注入的核心。越是大型的項(xiàng)目,越難以管理依賴關(guān)系,開發(fā)工作逐漸變化為一個個節(jié)點(diǎn)的開發(fā),而這些節(jié)點(diǎn)通過依賴注入關(guān)聯(lián)起來。依賴注入降低了開發(fā)的成本、提高了代碼的復(fù)用率、提高了軟件的靈活性,也給軟件開發(fā)帶來了挑戰(zhàn),你根本不知道運(yùn)行時容器會給你什么。

理解DI的關(guān)鍵是:“誰依賴誰?為什么需要依賴?誰注入誰?注入了什么”,那我們來深入分析一下:

  1. 誰依賴于誰:當(dāng)然是應(yīng)用程序依賴于IOC容器。
  2. 為什么需要依賴:應(yīng)用程序需要IOC容器來提供對象需要的外部資源。
  3. 誰注入誰:很明顯是IOC容器注入應(yīng)用程序某個對象,應(yīng)用程序依賴的對象。
  4. 注入了什么:就是注入某個對象所需要的外部資源(包括對象、資源、常量數(shù)據(jù))。

解讀:
DI 即依賴注入,重點(diǎn)就在于 “依賴”、“注入” 兩個概念。什么是依賴?對象運(yùn)行所需要的外部的數(shù)據(jù)、資源就是依賴,沒有這些東西對象不能完成業(yè)務(wù)處理,必須拿到才能運(yùn)行。什么是注入?注入這個詞真的很形象,就像打針一樣,從外部注入到內(nèi)部,容器加載了外部的文件、URL、配置和對象然后把這些數(shù)據(jù)、對象按需注入給對象。

IOC和DI有什么關(guān)系呢?其實(shí)它們是同一個概念的不同角度描述,由于控制反轉(zhuǎn)概念比較含糊,所以2004年大師級人物Martin Fowler又給出了一個新的名字:“依賴注入”,相對IOC 而言,“依賴注入”明確描述了“被注入對象依賴IOC容器配置依賴對象”。

解讀:
IOC 和 DI 是同一個概念的不同角度描述,但實(shí)際上又是有區(qū)別的。IOC 強(qiáng)調(diào)的是容器和對象的控制權(quán)發(fā)生了反轉(zhuǎn),而 DI 強(qiáng)調(diào)的是對象的依賴由容器進(jìn)行注入,大部分情況下說兩者相同也不算錯。 但是廣義上 IOC 是一種軟件開發(fā)模式,也就是說還可以通過別的方式實(shí)現(xiàn),而 DI 只是其中一種,Spring 選擇了 DI,從而使 DI 在 Java 開發(fā)中深入人心。

四、IOC原理

Spring中的IOC的實(shí)現(xiàn)原理就是工廠模式反射機(jī)制。

  1. 不使用反射機(jī)制的工廠模式
 public class ReflectFactory {
    /**
     * 工廠模式     
     */    
    interface fruit{      
        public abstract void eat();    
    }    
    class Apple implements fruit{      
        public void eat(){       
            System.out.println("Apple"); 
        }   
    }    
    class Orange implements fruit{    
        public void eat(){      
            System.out.println("Orange"); 
        }    
    } 
    // 構(gòu)造工廠類    
    // 也就是說以后如果我們在添加其他的實(shí)例的時候只需要修改工廠類就行了   
    public fruit getInstance(String fruitName){     
        fruit f=null;         
        if("Apple".equals(fruitName)){     
            f=new Apple();        
        }          
        if("Orange".equals(fruitName)){   
            f=new Orange();     
        }
        return f; 
    }
    public static void main(String[] args){
        ReflectFactory rf = new ReflectFactory();
        fruit f=rf.getInstance("Orange"); 
        f.eat();     
    }
}

當(dāng)我們再添加一個子類的時候,就需要修改工廠類了。如果我們添加太多的子類的時候,改的就會很多。

  1. 利用反射機(jī)制的工廠模式
public class ReflectFactory {
    interface fruit{    
        public abstract void eat(); 
    }   
    class Apple implements fruit{    
        public void eat(){      
            System.out.println("Apple");  
        }   
    }   
    class Orange implements fruit{  
        public void eat(){    
            System.out.println("Orange");   
        }   
    }  
    public static fruit getInstance(String ClassName){  
        fruit f=null;       
        try{         
            f=(fruit)Class.forName(ClassName).newInstance();   
        }catch (Exception e) {      
            e.printStackTrace();      
        }        return f;   
    }   
    public static void main(String[] args) {
        fruit f=ReflectFactory.getInstance("Reflect.Apple"); 
        if(f!=null){        
            f.eat();   
        }    
    }    
}

現(xiàn)在就算我們添加任意多個子類的時候,工廠類就不需要修改。

使用反射機(jī)制的工廠模式可以通過反射取得接口的實(shí)例,但是需要傳入完整的包和類名。而且用戶也無法知道一個接口有多少個可以使用的子類,所以我們通過屬性文件的形式配置所需要的子類。

  1. 使用反射機(jī)制并結(jié)合屬性文件的工廠模式(即IoC)

先創(chuàng)建一個fruit.properties的資源文件:

apple=Reflect.Apple    orange=Reflect.Orange

然后編寫主類代碼:

public class ReflectFactory {

    interface fruit{  
        public abstract void eat();  
    }   
    class Apple implements fruit{   
        public void eat(){      
            System.out.println("Apple");  
        }    
    }   
    class Orange implements fruit{  
        public void eat(){     
            System.out.println("Orange"); 
        }  
    }  
    public static fruit getInstance(String ClassName){ 
        fruit f=null;     
        try{         
            f=(fruit)Class.forName(ClassName).newInstance(); 
        }catch (Exception e) {       
            e.printStackTrace();  
        }       
        return f;   
    }  
    public static void main(String[] args) throws FileNotFoundException, IOException{
        Properties pro=init.getPro();    
        fruit f=ReflectFactory.getInstance(pro.getProperty("apple"));  
        if(f!=null){        
            f.eat(); 
        }      
    }
}    
//[運(yùn)行結(jié)果]:Apple

操作屬性文件類:

public class init {
    public static Properties getPro() throws FileNotFoundException, IOException{  
        Properties pro=new Properties();    
        File f=new File("fruit.properties"); 
        if(f.exists()){          
            pro.load(new FileInputStream(f));  
        }else{           
            pro.setProperty("apple", "Reflect.Apple");   
            pro.setProperty("orange", "Reflect.Orange");   
            pro.store(new FileOutputStream(f), "FRUIT CLASS"); 
        }        return pro;    
    } 
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容