什麼是依賴注入

來源:互聯網
上載者:User

標籤:des   style   http   color   io   使用   java   strong   ar   

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的介面
//定義Person介面
public interface Person
{
//Person介面裡定義一個使用斧子的方法
public void useAxe();
}

然後Axe的介面
//定義Axe介面
public interface Axe
{
//Axe介面裡有個砍的方法
public void chop();
}

Person的實作類別
//Chinese實現Person介面

public class Chinese implements Person
{
//面向Axe介面編程,而不是詳細的實作類別
private Axe axe;
//預設的構造器
public Chinese()
{}
//設值注入所需的setter方法
public void setAxe(Axe axe)
{
this.axe = axe;
}
//實現Person介面的useAxe方法
public void useAxe()
{
System.out.println(axe.chop());
}
}

Axe的第一個實作類別
//Axe的第一個實作類別 StoneAxe

public class StoneAxe implements Axe
{
//預設構造器
public StoneAxe()
{}
//實現Axe介面的chop方法
public String chop()
{
return "石斧砍柴好慢";
}
}

以下採用Spring的設定檔將Person執行個體和Axe執行個體組織在一起。設定檔例如以下所看到的:
<!-- 以下標準的XML檔案頭 -->
<?xml version="1.0" encoding="gb2312"?>
<!-- 以下一行定義Spring的XML設定檔的dtd -->
"http://www.springframework.org/dtd/spring-beans.dtd">
<!-- 以上三行對全部的Spring設定檔都同樣的 -->
<!-- Spring設定檔的根項目 -->
<BEANS>
<!—定義第一bean,該bean的idchinese, class指定該bean執行個體的實作類別 -->
<BEAN class=lee.Chinese id=chinese>
<!-- property元素用來指定須要容器注入的屬性,axe屬性須要容器注入此處設值注入,因此Chinese類必須擁有setAxe方法 -->
<property name="axe">
<!-- 此處將還有一個bean的引用注入給chinese bean -->
<REF local="”stoneAxe”/">
</property>
</BEAN>
<!-- 定義stoneAxe bean -->
<BEAN class=lee.StoneAxe id=stoneAxe />
</BEANS>

從設定檔中,能夠看到Spring管理bean的機靈性。bean與bean之間的依賴關係放在設定檔中組織,而不是寫在代碼裡。通過設定檔的 指定,Spring能精確地為每一個bean注入屬性。因此,設定檔中的bean的class元素,不能只介面,而必須真正的實作類別。

Spring會自己主動接管每一個bean定義裡的property元素定義。Spring會在運行無參數的構造器後、建立預設的bean執行個體後,調用相應 的setter方法為程式注入屬性值。property定義的屬性值將不再由該bean來主動建立、管理,而改為被動接收Spring的注入

每一個bean的id屬性該bean的惟一標識,程式通過id屬性訪問bean,bean與bean的依賴關係也通過id屬性完畢。

以下看主程式部分:
public class BeanTest
{
//主方法,程式的入口
public static void main(String[] args)throws Exception
{
//由於獨立的應用程式,顯式地執行個體化Spring的上下文。
ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
//通過Person bean的id來擷取bean執行個體,面向介面編程,因此
//此處強制類型轉換為介面類型
Person p = (Person)ctx.getBean("chinese");
//直接運行Person的userAxe()方法。
p.useAxe();
}
}

程式的執行結果例如以下:

石斧砍柴好慢

主程式調用Person的useAxe()方法時,該方法的方法體內須要使用Axe的執行個體,但程式裡沒有不論什麼地方將特定的Person執行個體和Axe實 例耦合在一起。或者說,程式裡沒有為Person執行個體傳入Axe的執行個體,Axe執行個體由Spring在執行期間動態注入

Person執行個體不僅不須要瞭解Axe執行個體的詳細實現,甚至無須瞭解Axe的建立過程。程式在執行到須要Axe執行個體的時候,Spring建立了Axe 執行個體,然後注入給須要Axe執行個體的調用者。Person執行個體執行到須要Axe執行個體的地方,自然就產生了Axe執行個體,用來供Person執行個體使用。

調用者不僅無須關心被調用者的實現過程,連工廠定位都能夠省略(真是按需分配啊!)。以下也給出使用Ant編譯和執行該應用的簡單指令碼:
<?xml version="1.0"?>
<!-- 定義編譯該項目的基本資料-->
<PROJECT name="spring" default="." basedir=".">
<!-- 定義編譯和執行該項目時所需的庫檔案 -->
<PATH id=classpath>
<!-- 該路徑下存放spring.jar和其它第三方類庫 -->
<FILESET dir=../../lib>
<INCLUDE name="*.jar" />
</FILESET>
<!-- 同一時候還須要引用已經編譯過的class檔案-->
<PATHELEMENT path="." />
</PATH>
<!-- 編譯所有的java檔案-->
<TARGET description="Compile all source code" name="compile">
<!-- 指定編譯後的class檔案的存放位置 -->
<JAVAC debug="true" destdir=".">
deprecation="false" optimize="false" failonerror="true">
<!-- 指定須要編譯的源檔案的存放位置 -->
<SRC path="." />
<!-- 指定編譯這些java檔案須要的類庫位置-->
<CLASSPATH refid="classpath" />
</JAVAC>
</TARGET>
<!-- 執行特定的主程式 -->
<TARGET description="run the main class" name="run" depends="compile">
<!-- 指定執行的主程式:lee.BeanTest。-->
<JAVA failonerror="true" fork="yes" classname="lee.BeanTest">
<!-- 指定執行這些java檔案須要的類庫位置-->
<CLASSPATH refid="classpath" />
</JAVA>
</TARGET>
</PROJECT>

假設須要改寫Axe的實作類別。或者說,提供還有一個實作類別給Person執行個體使用。Person介面、Chinese類都無須改變。僅僅需提供還有一個Axe的實現,然後對設定檔進行簡單的改動就可以。

Axe的還有一個實現例如以下:
//Axe的還有一個實作類別 SteelAxe
public class SteelAxe implements Axe
{
//預設構造器
public SteelAxe()
{}
//實現Axe介面的chop方法
public String chop()
{
return "鋼斧砍柴真快";
}
}

然後,改動原來的Spring設定檔,在當中添加?例如以下一行:
<!-- 定義一個steelAxe bean-->
<BEAN class=lee.SteelAxe id=steelAxe />

該行又一次定義了一個Axe的實現:SteelAxe。然後改動chinese bean的配置,將原來傳入stoneAxe的地方改為傳入steelAxe。也就是將
<REF local="”stoneAxe”/">

改成
<REF local="”steelAxe”/">

此時再次運行程式,將得到例如以下結果:

鋼斧砍柴真快

Person與Axe之間沒有不論什麼代碼耦合關係,bean與bean之間的依賴關係由Spring管理。採用setter方法為目標bean注入屬性的方式,稱為設值注入

業務對象的更換變得相當簡單,對象與對象之間的依賴關係從代碼裡分離出來,通過設定檔動態管理。構造注入

  所謂構造注入,指通過建構函式來完畢依賴關係的設定,而不是通過setter方法。對前面代碼Chinese類做簡單的改動,改動後的代碼例如以下:
//Chinese實現Person介面
public class Chinese implements Person
{
//面向Axe介面編程,而不是詳細的實作類別
private Axe axe;
//預設的構造器
public Chinese()
{}
//構造注入所需的帶參數的構造器
public Chinse(Axe axe)
{
this.axe = axe;
}
//實現Person介面的useAxe方法
public void useAxe()
{
System.out.println(axe.chop());
}
}

此時無須Chinese類裡的setAxe方法,構造Person執行個體時,Spring為Person執行個體注入依賴的Axe執行個體。構造注入的設定檔也需做簡單的改動,改動後的設定檔例如以下:
<!-- 以下標準的XML檔案頭 -->
<xml version="1.0" encoding="gb2312"?>
<!-- 以下一行定義Spring的XML設定檔的dtd -->
"http://www.springframework.org/dtd/spring-beans.dtd">
<!-- 以上三行對全部的Spring設定檔都同樣的 -->
<!-- Spring設定檔的根項目 -->
<BEANS>
<!—定義第一個bean,該bean的idchinese, class指定該bean執行個體的實作類別 -->
<BEAN class=lee.Chinese id=chinese>
</BEAN>
<!-- 定義stoneAxe bean -->
<BEAN class=lee.SteelAxe id=steelAxe />
</BEANS>

運行效果與使用steelAxe設值注入時的運行效果全然一樣。差別在於:建立Person執行個體中Axe屬性的時機不同——設值注入現建立一個預設的bean執行個體,然後調用相應的構造方法注入依賴關係。而構造注入則在建立bean執行個體時,已經完畢了依賴關係的

什麼是依賴注入

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.