Spring--依賴注入

Spring 能有效地組織J2EE應用各層的對象。不論是控 制層的Action對象,仍是業務層的Service對象,仍是持久層的DAO對象,均可在Spring的 管理下有機地協調、運行。Spring將各層的對象以鬆耦合的方式組織在一塊兒,Action對象無須關心Service對象的具體實現,Service對 象無須關心持久層對象的具體實現,各層對象的調用徹底面向接口。當系統須要重構時,代碼的改寫量將大大減小。

上面所說的一切都得宜於Spring的核心機制,依賴注入。依賴注入讓bean與bean之間以配置文件組織在一塊兒,而不是以硬編碼的方式耦合在一塊兒。理解依賴注入
 
依賴注入(Dependency Injection)和控制反轉(Inversion of Control)是同一個概念。具體含義是:當某個角色(多是一 個Java實例,調用者)須要另外一個角色(另外一個Java實例,被調用者)的協助時,在 傳統的程序設計過程當中,一般由調用者來建立被調用者的實例。但在Spring裏,建立被調用者的工做再也不由調用者來完成,所以稱爲控制反轉;建立被調用者 實例的工做一般由Spring容器來完成,而後注入調用者,所以也稱爲依賴注入。

不論是依賴注入,仍是控制反轉,都說明Spring採用動態、靈活的方式來管理各類對象。對象與對象之間的具體實現互相透明。在理解依賴注入以前,看以下這個問題在各類社會形態裏如何解決:一我的(Java實例,調用者)須要一把斧子(Java實例,被調用者)。

(1)原始社會裏,幾乎沒有社會分工。須要斧子的人(調用者)只能本身去磨一把斧子(被調用者)。對應的情形爲:Java程序裏的調用者本身建立被調用者。

(2)進入工業社會,工廠出現。斧子再也不由普通人完成,而在工廠裏被生產出來,此時須要斧子的人(調用者)找到工廠,購買斧子,無須關心斧子的製造過程。對應Java程序的簡單工廠的設計模式。

(3)進入「按需分配」社會,須要斧子的人不須要找到工廠,坐在家裏發出一個簡單指令:須要斧子。斧子就天然出如今他面前。對應Spring的依賴注入。

第一種狀況下,Java實例的調用者建立被調用的Java實例,必然要求被調用的Java類出如今調用者的代碼裏。沒法實現兩者之間的鬆耦合。

第二種狀況下,調用者無須關心被調用者具體實現過程,只須要找到符合某種標準(接口)的實例,便可使用。此時調用的代碼面向接口編程,可讓調用者和被調用者解耦,這也是工廠模式大量使用的緣由。但調用者須要本身定位工廠,調用者與特定工廠耦合在一塊兒。

第三種狀況下,調用者無須本身定位工廠,程序運行到須要被調用者時,系統自動提供被調用者實例。事實上,調用者和被調用者都處於Spring的管理下,兩者之間的依賴關係由Spring提供。

所謂依賴注入,是指程序運行過程當中,若是須要調用另外一個對象協助時,無須在代碼中建立被調用者,而是依賴於外部的注入。Spring的依賴注入對調用者和被調用者幾乎沒有任何要求,徹底支持對POJO之間依賴關係的管理。依賴注入一般有兩種:

·設值注入。
·構造注入。
 
 設值注入是指經過setter方法傳入被調用者的實例。這種注入方式簡單、直觀,於是在Spring的依賴注入裏大量使用。看下面代碼,是Person的接口
1 //定義Person接口
2 public interface Person
3 {
4 //Person接口裏定義一個使用斧子的方法
5 public void useAxe();
6 }

而後是Axe的接口
1 //定義Axe接口
2 public interface Axe
3 {
4 //Axe接口裏有個砍的方法
5 public void chop();
6 }

 


Person的實現類
 1 //Chinese實現Person接口
 2 
 3 public class Chinese implements Person
 4 {
 5 //面向Axe接口編程,而不是具體的實現類
 6 private Axe axe;
 7 //默認的構造器
 8 public Chinese()
 9 {}
10 //設值注入所需的setter方法
11 public void setAxe(Axe axe)
12 {
13 this.axe = axe;
14 }
15 //實現Person接口的useAxe方法
16 public void useAxe()
17 {
18 System.out.println(axe.chop());
19 }
20 }
Axe的第一個實現類
 1 //Axe的第一個實現類 StoneAxe
 2 
 3 public class StoneAxe implements Axe
 4 {
 5 //默認構造器
 6 public StoneAxe()
 7 {}
 8 //實現Axe接口的chop方法
 9 public String chop()
10 {
11 return "石斧砍柴好慢";
12 }
13 }
下面採用Spring的配置文件將Person實例和Axe實例組織在一塊兒。配置文件以下所示:
 1 <!-- 下面是標準的XML文件頭 -->
 2 <?xml version="1.0" encoding="gb2312"?>
 3 <!-- 下面一行定義Spring的XML配置文件的dtd -->
 4 "http://www.springframework.org/dtd/spring-beans.dtd">
 5 <!-- 以上三行對全部的Spring配置文件都是相同的 -->
 6 <!-- Spring配置文件的根元素 -->
 7 <BEANS>
 8 <!—定義第一bean,該bean的id是chinese, class指定該bean實例的實現類 -->
 9 <BEAN class="lee".Chinese id=chinese>
10 <!-- property元素用來指定須要容器注入的屬性,axe屬性須要容器注入此處是設值注入,所以Chinese類必須擁有setAxe方法 -->
11 
12 <property name="axe">
13 <!-- 此處將另外一個bean的引用注入給chinese bean -->
14 <REF local="」stoneAxe」/">
15 </property>
16 </BEAN>
17 <!-- 定義stoneAxe bean -->
18 <BEAN class="lee".StoneAxe id=stoneAxe />
19 </BEANS>
 
 

從配置文件中,能夠看到Spring管理bean的靈巧性。bean與bean之間 的依賴關係放在配置文件裏組織,而不是寫在代碼裏。經過配置文件的 指定,Spring能精確地爲每一個bea n注入屬性。所以,配置文件裏的bean的class元素,不能僅 僅是接口,而必 須是真正的實現類。

Spring會自動接管每一個bean定義裏的property元素定義。Spring會在執行無參數的構造器後、建立默認的bean實例後,調用對應 的setter方法爲程 序注入屬性值。property定義的屬性值將再也不由該bean來主動建立、管理,而改成被動接收Spring 的注入

每一個bean的id屬 性是該bean的唯一標識,程序經過id屬性訪問bean,bean與bean 的依賴關係也經過id屬性完成。

下面看主程序部分:
 1 public class BeanTest
 2 {
 3 //主方法,程序的入口
 4 public static void main(String[] args)throws Exception
 5 {
 6 //由於是獨立的應用程序,顯式地實例化Spring的上下文。
 7 ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
 8 //經過Person bean的id來獲取bean實例,面向接口編程,所以
 9 //此處強制類型轉換爲接口類型
10 Person p = (Person)ctx.getBean("chinese");
11 //直接執行Person的userAxe()方法。
12 p.useAxe();
13 }
14 }

程序的執行結果以下:

石斧砍柴好慢

主程序調用Person的useAxe()方法時,該方法的方法體內須要使用Axe的實例,但程序裏沒有任何地方將特定的Person實例和Axe實 例耦合在一塊兒。或者說,程序裏沒有爲Person實例傳入Axe的實例,Axe實例由Spring在運行期間動 態注入

Person實例不只不須要了解Axe實例的具體實現,甚至無須瞭解Axe的建立過程。程序在運行到須要Axe實例的時候,Spring建立了Axe 實例,然 後注入給須要Axe實例的調用者。Person實例運行到須要Axe實例的地方,天然就產生了Axe實例,用來供Person實例使用。

調用者不只無須關心被調用者的實現過程,連工廠定位均可以省略(真是按需分配啊!)。下面也給出使用Ant編譯和運行該應用的簡單腳本:
 1 <?xml version="1.0"?>
 2 <!-- 定義編譯該項目的基本信息-->
 3 <PROJECT name="spring" default="." basedir=".">
 4 <!-- 定義編譯和運行該項目時所需的庫文件 -->
 5 <PATH id=classpath>
 6 <!-- 該路徑下存放spring.jar和其餘第三方類庫 -->
 7 <FILESET dir=../../lib>
 8 <INCLUDE name="*.jar" />
 9 </FILESET>
10 <!-- 同時還須要引用已經編譯過的class文件-->
11 <PATHELEMENT path="." />
12 </PATH>
13 <!-- 編譯所有的java文件-->
14 <TARGET description="Compile all source code" name="compile">
15 <!-- 指定編譯後的class文件的存放位置 -->
16 <JAVAC debug="true" destdir=".">
17 deprecation="false" optimize="false" failonerror="true">
18 <!-- 指定須要編譯的源文件的存放位置 -->
19 <SRC path="." />
20 <!-- 指定編譯這些java文件須要的類庫位置-->
21 <CLASSPATH refid="classpath" />
22 </JAVAC>
23 </TARGET>
24 <!-- 運行特定的主程序 -->
25 <TARGET description="run the main class" name="run" depends="compile">
26 <!-- 指定運行的主程序:lee.BeanTest。-->
27 <JAVA failonerror="true" fork="yes" classname="lee.BeanTest">
28 <!-- 指定運行這些java文件須要的類庫位置-->
29 <CLASSPATH refid="classpath" />
30 </JAVA>
31 </TARGET>
32 </PROJECT>

若是須要改寫Axe的實現類。或者說,提供另外一個實現類給Person實例使用。Person接口、Chinese類都無須改變。只需提供另外一個Axe的實現,而後對配置文件進行簡單的修改便可。

Axe的另外一個實現以下:
 1 //Axe的另外一個實現類 SteelAxe
 2 public class SteelAxe implements Axe
 3 {
 4 //默認構造器
 5 public SteelAxe()
 6 {}
 7 //實現Axe接口的chop方法
 8 public String chop()
 9 {
10 return "鋼斧砍柴真快";
11 }
12 }

而後,修改原來的Spring配置文件,在其中增長以下一行:
1 <!-- 定義一個steelAxe bean-->
2 <BEAN class="lee".SteelAxe id=steelAxe />

該行從新定義了一個Axe的實現:SteelAxe。而後修改chinese bean的配置,將原來傳入stoneAxe的地方改成傳入steelAxe。也就是將
<REF local="」stoneAxe」/">
改爲
<REF local="」steelAxe」/">
此時再次執行程序,將獲得以下結果:
鋼斧砍柴真快
Person與Axe之間沒有任何代碼耦合關係,bean與bean之間 的依賴關係由Spring管理。採用setter方法爲目標bea n注入屬性的方式,稱爲設 值注入
業務對象的更換變得至關簡單,對象與對象之間 的依賴關係從代碼裏分離出來,經過配置文件動態管理。
構造注入
  所謂構 造注入,指經過構造函數來完 成依賴關係的設定,而不是經過setter方法。對前面代碼Chinese類作簡單的修改,修改後的代碼以下:
 1 //Chinese實現Person接口
 2 public class Chinese implements Person
 3 {
 4 //面向Axe接口編程,而不是具體的實現類
 5 private Axe axe;
 6 //默認的構造器
 7 public Chinese()
 8 {}
 9 //構造注入所需的帶參數的構造器
10 public Chinse(Axe axe)
11 {
12 this.axe = axe;
13 }
14 //實現Person接口的useAxe方法
15 public void useAxe()
16 {
17 System.out.println(axe.chop());
18 }
19 }

此時無須Chinese類裏的setAxe方法,構造Person實例時,Spring爲Person實例注入所依賴的Axe實例。構造注入的配置文件也需作簡單的修改,修改後的配置文件以下:
 1 <!-- 下面是標準的XML文件頭 -- 2 <xml version="1.0" encoding="gb2312"? 3 <!-- 下面一行定義Spring的XML配置文件的dtd -- 4 "http://www.springframework.org/dtd/spring-beans.dtd" 5 <!-- 以上三行對全部的Spring配置文件都是相同的 -- 6 <!-- Spring配置文件的根元素 -- 7 <BEANS>
 8 <!—定義第一個bean,該bean的id是chinese, class指定該bean實例的實現類 -- 9 <BEAN class="lee".Chinese id=chinese>
10 </BEAN>
11 <!-- 定義stoneAxe bean --12 <BEAN class="lee".SteelAxe id=steelAxe />
13 </BEANS>

執行效果與使用steelAxe設值 注入時的執行效果徹底同樣。區別在於:建立Person實例中Axe屬性的時機不一樣——設值 注入 現建立一個默認的bean實例,而後調用對應的構造方法 注入 依賴關係。而構造 注入則在建立bean實例時,已經完成了 依賴關係的
相關文章
相關標籤/搜索