上篇文章(java框架學習日誌-1)雖然跟着寫了例子,也理解爲何這麼寫,可是有個疑問,爲何叫控制反轉?控制的是什麼?反轉又是什麼?
控制其實就是控制對象的建立。
反轉與正轉對應,正轉是由程序自己來建立對象,而反轉就是程序自己不建立對象。而是被動地接收對象。
上篇文章雖然把對象的建立這一步移至客戶端,可是終究仍是由程序來建立對象的。Spring提供了一個Ioc容器,能夠用它來建立對象,在spring中,全部java類都是資源,全部資源都是Bean,管理這些Bean的就是Spring提供的Ioc容器。
若是不使用Ioc容器去生成對象,那麼全部對象都要靠程序自己建立,若是測試人員要測試一個類,就要本身去構建這個類,和這個類所依賴的其餘類,可是測試人員對構建對象並不熟悉,並且也會加大工做量。
若是不使用Ioc容器會怎麼樣呢?舉個例子,咱們若是要吃飯,那麼就要本身煮飯,本身作菜,本身作飲料。這就是主動去建立對象,可是在有了Ioc容器後,Ioc容器就像飯店,咱們只須要描述本身須要什麼,而後就不用關注作飯的具體過程,而後等飯端上來就行了,這就是被動地接收對象,下面是代碼演示。java
首先須要一個鍋,而後準備米飯,飲料,肉。spring
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,後面要用到hello的時候我分不清楚傳的究竟是哪一個,因此這裏我就用了不同的名字。工具
<?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容器來管理和建立對象,好比再添加一頓飯就能夠這樣添加。ui
<?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等屬性的值是由容器給的,這叫依賴注入,實際上是一個東西,只是看的角度不同。this
其實ioc是一種編程思想,它的目的也是遵循了設計模式中的下降程序耦合度,方便拓展,避免修改。上面例子中是將對象的建立脫離出程序自己,讓spring去完成,其實這只是一方面,主要思想是將主動編程變成被動接受。本身去new對象也是主動編程,接收對象也是接收資源。 Ioc的實現是經過ioc容器實現的,ioc容器就是bean工廠(由於bean能夠建立不少對象,並且因此也叫bean工廠。bean工廠其實也是工廠模式,只是這種工廠模式是由spring提供,以前看設計模式,覺得這些設計模式都要本身寫,原來也有輪子能夠用,好比單例模式,只要在beans.xml裏的class="FoodMaker"後面加上scope="single"。代碼以下
<bean name="cooking" class="FoodMaker" scope="singleton"> <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文件時不會實時糾錯,而系統給的錯誤信息又太多看不懂,因此排錯排了好久,才發現是大小寫錯了。最後再測試一下這個代碼,運行結果和以前同樣,就不貼圖了。