上篇文章(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文件時不會實時報錯,而系統給的錯誤信息又太多看不懂,所以排錯排了很久,才發現是大小寫錯了。最後這個再測試一下這個代碼,運行結果和之前一樣,就不貼圖了。