java框架學習日誌-2

上篇文章(java框架學習日誌-1)雖然跟着寫了例子,也理解爲什麼這麼寫,但是有個疑問,爲什麼叫控制反轉?控制的是什麼?反轉又是什麼?
控制其實就是控制對象的創建。
反轉與正轉對應,正轉是由程序本身來創建對象,而反轉就是程序本身不創建對象。而是被動地接收對象。
上篇文章雖然把對象的創建這一步移至客戶端,但是終究還是由程序來創建對象的。Spring提供了一個Ioc容器,可以用它來創建對象,在spring中,所有java類都是資源,所有資源都是Bean,管理這些Bean的就是Spring提供的Ioc容器。
如果不使用Ioc容器去生成對象,那麼所有對象都要考程序本身創建,如果測試人員要測試一個類,就要自己去構建這個類,和這個類所依賴的其他類,但是測試人員對構建對象並不熟悉,而且也會加大工作量。
如果不使用Ioc容器會怎麼樣呢?舉個例子,我們如果要吃飯,那麼就要自己煮飯,自己做菜,自己做飲料。這就是我主動去創建對象,但是在有了Ioc容器後,Ioc容器就像飯店,我們只需要描述自己需要什麼,然後就不用關注做飯的具體過程,然後等飯端上來就好了,這就是被動地接收對象,下面是代碼演示。

主動創建對象

首先需要一個鍋,然後準備米飯,飲料,肉。

public class Pan {

    /**
     *鍋
     * @param rice 加米飯
     * @param juice 喝什麼飲料
     * @param meat 吃什麼肉
     * @return 成品
     */
    public String cooking(String rice,String juice,String meat){
        String food=rice+"配"+meat+",再喝點"+juice;
        return food;
    }
}

然後是烹飪過程,需要new一個鍋,傳入米飯等參數的具體值

public class FoodMaker {
    private Pan pan=new Pan();
    private String rice;
    private String juice;
    private String meat;

    public String getRice() {
        return rice;
    }

    public void setRice(String rice) {
        this.rice = rice;
    }

    public String getJuice() {
        return juice;
    }

    public void setJuice(String juice) {
        this.juice = juice;
    }

    public String getMeat() {
        return meat;
    }

    public void setMeat(String meat) {
        this.meat = meat;
    }

    public String makefood(){
        return pan.cooking(rice,juice,meat);
    }
}

最後測試一下

public class Test {
    public static void main(String[] args) {
        FoodMaker foodMaker=new FoodMaker();
        foodMaker.setRice("白米飯");
        foodMaker.setJuice("可樂");
        foodMaker.setMeat("黃燜雞");
        String food=foodMaker.makefood();
        System.out.println(food);
    }
}


然後用被動創建對象來實現上面的代碼。

被動創建對象

在寫代碼之前需要先導入相關jar包,這個可以去spring官網下載,既然要用bean去管理java類,那麼就需要一個bean的xml文件,在src目錄下新建xml文件

既然用的別人的東西,那麼就需要導入頭文件,spring的頭文件,模板如下,現在不用管爲什麼這麼寫,複製就對了,之前提到過,bean就是java對象,由spring容易來創建和管理,這裏的name可以換成id。id是唯一的,name是別名。class就是類的全名,包括位置,因爲我這裏沒寫包,所以就只是類名。property就是類裏的參數,value就是參數的值。在教學視頻中bean的name和類名都是hello,後面要用到hell的時候我分不清楚傳的到底是哪個,所以這裏我就用了不一樣的名字。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    <bean name="cooking" class="FoodMaker">
        <property name="rice" value="土豆飯"/>
        <property name="juice" value="紅茶"/>
        <property name="meat" value="小炒肉"/>
    </bean>
</beans>

然後就可以測試一下了

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        //解析beans.xml文件,生成相應對象,傳入bean容器的名字
        ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
        FoodMaker foodMaker=(FoodMaker)context.getBean("cooking");//cooking就是FoodMaker的別名
        System.out.println(foodMaker.makefood());
    }
}

結果。注意結果是通過commons logging打印的,這個需要的jar包同樣在spring裏面

從結果上來看,兩種寫法是一樣的,但是通過spring來實現代碼,就不需要程序來創建對象,對象是由spring容器創建的,對象的屬性是由spring容器來設置的。這麼一個過程就叫控制反轉。這樣的話,如果以後需要添加對象,就不用修改代碼,可以直接通過bean容器來管理和創建對象,比如再添加一頓飯就可以這樣添加

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    <bean name="cooking" class="FoodMaker">
        <property name="rice" value="土豆飯"/>
        <property name="juice" value="紅茶"/>
        <property name="meat" value="小炒肉"/>
    </bean>
    <bean name="cooking2" class="FoodMaker">
        <property name="rice" value="炒飯"/>
        <property name="juice" value="肥仔快樂水"/>
        <property name="meat" value="肥仔快樂堡"/>
    </bean>
</beans>

測試結果
控制反轉還有個名字叫依賴注入。在上面的例子中,依賴指的是FoodMaker對rice,juice,meat的依賴。而rice等屬性的值有事容器給的,這叫依賴注入,其實是一個東西,只是看的角度不一樣。

總結

其實ioc是一種編程思想,它的目的也是遵循了設計模式中的降低程序耦合度,方便拓展,避免修改。上面例子中是將對象的創建脫離出程序本身,讓spring去完成,其實這只是一方面而且,主要思想是將主動編程變成被動接受。自己去new對象也是主動編程,接收對象也是接收資源。 Ioc的實現是通過ioc容器實現的,ioc容器就是bean工廠(因爲bean可以創建很多對象,而且所以也叫bean工廠。bean工廠其實也是工廠模式,只是這種工廠模式是由spring提供,之前看設計模式,以爲這些設計模式都要自己寫,原來也有輪子可以用,比如單例模式,只要在beans.xml裏的class="FoodMaker"後面加上scope="single"。代碼如下

<bean name="cooking" class="FoodMaker" scope="single">
        <property name="rice" value="土豆飯"/>
        <property name="juice" value="紅茶"/>
        <property name="meat" value="小炒肉"/>
    </bean>

如果創建了多個對象,就會報錯

上面的例子其實使用bean來創建對象還不夠徹底,在FoodMaker類裏面,還有個Pan對象是我們手動創建的。之前在只是簡單的給一些String屬性賦值,那如果是複雜一點的對象怎麼賦值呢?代碼如下:
修改在FoodMaker的代碼,讓pan的值爲null,生成set,get方法,通過set,get方法來給pan賦值。

    private Pan pan=null;
    private String rice;
    private String juice;
    private String meat;

    public Pan getPan() {
        return pan;
    }

    public void setPan(Pan pan) {
        this.pan = pan;
    }

beans.xml佈置文件如下,其中ref是引用對象,由spring創建。以後如果添加其他烹飪工具,只需要用spring創建對象,然後傳入ref就可以了。就不需要改變代碼了。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    <bean id="pan" class="Pan"/>
    <bean name="cooking" class="FoodMaker">
        <property name="rice" value="土豆飯"/>
        <property name="juice" value="紅茶"/>
        <property name="meat" value="小炒肉"/>
        <property name="pan" ref="pan"></property>
    </bean>
</beans>

注意!!!

public class FoodMaker {
    private Pan pan=null;//id=“pan”的pan不是這裏的pan。
    private String rice;
    private String juice;
    private String meat;

    public Pan getPan() {
        return pan;
    }

    public void setPan(Pan pan) {//注意在beans文件中,id=“pan”中的pan,其實setPan中的Pan首字母小寫。
        this.pan = pan;
    }

如果這裏setPan改成setPPP,那麼beans文件裏相應的id也要改成pPP,所以一定要注意書寫規範,我在寫這個例子的時候,因爲寫xml文件時不會實時報錯,而系統給的錯誤信息又太多看不懂,所以排錯排了很久,才發現是大小寫錯了。最後這個再測試一下這個代碼,運行結果和之前一樣,就不貼圖了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章