每一個(gè)基于java的應(yīng)用程序都有一個(gè)共同工作來展示給用戶看到的內(nèi)容作為工作的應(yīng)用幾個(gè)對(duì)象。當(dāng)編寫一個(gè)復(fù)雜的Java應(yīng)用程序,應(yīng)用程序類應(yīng)該盡可能獨(dú)立其他Java類來增加重復(fù)使用這些類,并獨(dú)立于其他類別的測試它們,而這樣做單元測試的可能性。依賴注入(或有時(shí)稱為布線)有助于粘合這些類在一起,同時(shí)保持他們的獨(dú)立。
考慮有其中有一個(gè)文本編輯器組件的應(yīng)用程序,要提供拼寫檢查。標(biāo)準(zhǔn)的代碼將看起來像這樣:
1
2
3
4
5
6
|
public class TextEditor { private SpellChecker spellChecker; public TextEditor() { spellChecker = new SpellChecker(); } } |
我們?cè)谶@里所做的就是創(chuàng)建文本編輯和拼寫檢查之間的依賴性。在控制方案中的反轉(zhuǎn),我們反而會(huì)做這樣的事情:
1
2
3
4
5
6
|
public class TextEditor { private SpellChecker spellChecker; public TextEditor(SpellChecker spellChecker) { this .spellChecker = spellChecker; } } |
在這里,文本編輯不應(yīng)該擔(dān)心拼寫檢查落實(shí)。拼寫檢查器將獨(dú)立實(shí)施,將提供給文本編輯在文本編輯實(shí)例化的時(shí)候,這整個(gè)過程是由Spring框架的控制。
在這里,我們已經(jīng)刪除從文本編輯的全面控制,并保持它在其他地方(即XML配置文件)和依賴性(即類拼寫檢查)被注入到類文本編輯通過類構(gòu)造函數(shù)。因此,流程控制已經(jīng)“倒”通過依賴注入(DI),因?yàn)橐呀?jīng)有效地委派依賴一些外部系統(tǒng)。
依賴注入的第二種方法是通過文本編輯類,我們將創(chuàng)建拼寫檢查實(shí)例的setter方法??,該實(shí)例將被用來調(diào)用setter方法??來初始化文本編輯的屬性。
因此,DI主要有兩種變體和下面的兩個(gè)子章將涵蓋兩者結(jié)合實(shí)例:
基于構(gòu)造函數(shù)的依賴注入
當(dāng)容器調(diào)用類的構(gòu)造函數(shù)有多個(gè)參數(shù),每個(gè)代表在其他類中的構(gòu)造函數(shù)依賴關(guān)系為基礎(chǔ)的DI來完成。
例子:
下面的例子顯示了一個(gè)類文本編輯TextEditor 只能是依賴注入與構(gòu)造函數(shù)注入。
我們使用Eclipse IDE,然后按照下面的步驟來創(chuàng)建一個(gè)Spring應(yīng)用程序:
這里是TextEditor.java文件的內(nèi)容:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package com.yiibai; public class TextEditor { private SpellChecker spellChecker; public TextEditor(SpellChecker spellChecker) { System.out.println( "Inside TextEditor constructor." ); this .spellChecker = spellChecker; } public void spellCheck() { spellChecker.checkSpelling(); } } |
下面是另外一個(gè)相關(guān)的類文件SpellChecker.java內(nèi)容:
1
2
3
4
5
6
7
8
9
10
11
12
|
package com.yiibai; public class SpellChecker { public SpellChecker(){ System.out.println( "Inside SpellChecker constructor." ); } public void checkSpelling() { System.out.println( "Inside checkSpelling." ); } } |
以下是MainApp.java文件的內(nèi)容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package com.yiibai; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainApp { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext( "Beans.xml" ); TextEditor te = (TextEditor) context.getBean( "textEditor" ); te.spellCheck(); } } |
以下是配置文件beans.xml文件里面有配置為基于構(gòu)造函數(shù)的注入:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<? 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-3.0.xsd"> <!-- Definition for textEditor bean --> < bean id = "textEditor" class = "com.yiibai.TextEditor" > < constructor-arg ref = "spellChecker" /> </ bean > <!-- Definition for spellChecker bean --> < bean id = "spellChecker" class = "com.yiibai.SpellChecker" > </ bean > </ beans > |
創(chuàng)建源代碼和bean配置文件完成后,讓我們運(yùn)行應(yīng)用程序。如果一切順利將打印以下信息:
1
2
3
|
Inside SpellChecker constructor. Inside TextEditor constructor. Inside checkSpelling. |
構(gòu)造函數(shù)的參數(shù)解析:
可能有歧義存在,而將參數(shù)傳遞給構(gòu)造函數(shù)的情況下有一個(gè)以上的參數(shù)。要解決這種不確定性,其中的構(gòu)造器參數(shù)在一個(gè)bean定義中定義的順序就是這些參數(shù)提供給適當(dāng)?shù)臉?gòu)造函數(shù)的順序。請(qǐng)考慮下面的類:
1
2
3
4
5
6
7
|
package x.y; public class Foo { public Foo(Bar bar, Baz baz) { // ... } } |
下面的配置工作正常:
1
2
3
4
5
6
7
8
9
|
<beans> <bean id= "foo" class = "x.y.Foo" > <constructor-arg ref= "bar" /> <constructor-arg ref= "baz" /> </bean> <bean id= "bar" class = "x.y.Bar" /> <bean id= "baz" class = "x.y.Baz" /> </beans> |
讓我們檢查一個(gè)更多情況下我們通過不同類型的構(gòu)造函數(shù)。請(qǐng)考慮下面的類:
1
2
3
4
5
6
7
|
package x.y; public class Foo { public Foo( int year, String name) { // ... } } |
容器也可以使用類型匹配與簡單類型,如果你明確地指定使用type屬性的構(gòu)造函數(shù)的參數(shù)類型。例如:
1
2
3
4
5
6
7
8
|
<beans> <bean id= "exampleBean" class = "examples.ExampleBean" > <constructor-arg type= "int" value= "2001" /> <constructor-arg type= "java.lang.String" value= "Zara" /> </bean> </beans> |
最后,并通過構(gòu)造函數(shù)參數(shù)的最佳方法,使用索引屬性來顯式地指定一個(gè)構(gòu)造器參數(shù)的索引。這里的索引是從0開始。例如:
1
2
3
4
5
6
7
8
|
<beans> <bean id= "exampleBean" class = "examples.ExampleBean" > <constructor-arg index= "0" value= "2001" /> <constructor-arg index= "1" value= "Zara" /> </bean> </beans> |
最后需要說明的,如果你傳遞一個(gè)引用到一個(gè)對(duì)象,需要使用<constructor-arg>標(biāo)簽的ref屬性,如果是直接傳遞一個(gè)值,那么應(yīng)該使用value屬性。
基于setter方法的依賴注入
基于setter DI由容器調(diào)用setter方法??對(duì)bean調(diào)用無參構(gòu)造器或無參static工廠方法實(shí)例化bean之后完成。
這里是TextEditor.java文件的內(nèi)容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package com.yiibai; public class TextEditor { private SpellChecker spellChecker; // a setter method to inject the dependency. public void setSpellChecker(SpellChecker spellChecker) { System.out.println( "Inside setSpellChecker." ); this .spellChecker = spellChecker; } // a getter method to return spellChecker public SpellChecker getSpellChecker() { return spellChecker; } public void spellCheck() { spellChecker.checkSpelling(); } } |
在這里,需要檢查setter方法??的命名約定。設(shè)置我們使用setSpellChecker()方法,這是非常類似于Java POJO類的變量的拼寫檢查器。讓我們創(chuàng)造另一個(gè)相關(guān)的類文件SpellChecker.java,內(nèi)容如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
package com.yiibai; public class SpellChecker { public SpellChecker(){ System.out.println( "Inside SpellChecker constructor." ); } public void checkSpelling() { System.out.println( "Inside checkSpelling." ); } } |
以下是MainApp.java文件的內(nèi)容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package com.yiibai; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainApp { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext( "Beans.xml" ); TextEditor te = (TextEditor) context.getBean( "textEditor" ); te.spellCheck(); } } |
以下是配置文件beans.xml文件里面有配置為基于setter方法??注入:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<? 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-3.0.xsd"> <!-- Definition for textEditor bean --> < bean id = "textEditor" class = "com.yiibai.TextEditor" > < property name = "spellChecker" ref = "spellChecker" /> </ bean > <!-- Definition for spellChecker bean --> < bean id = "spellChecker" class = "com.yiibai.SpellChecker" > </ bean > </ beans > |
應(yīng)該注意在基于構(gòu)造函數(shù)注入和setter注入定義beans.xml文件的差異。唯一的區(qū)別是,我們已經(jīng)使用<constructor-arg>標(biāo)簽為基于構(gòu)造函數(shù)的注入和的<property>標(biāo)簽為基于setter注入的<bean>元素內(nèi)。
需要注意的第二個(gè)重要的一點(diǎn)是,如果傳遞一個(gè)引用到一個(gè)對(duì)象,需要使用<property>標(biāo)簽的ref屬性,如果是直接傳遞一個(gè)值,那么應(yīng)該使用value屬性。
創(chuàng)建源代碼和bean配置文件完成后,讓我們運(yùn)行應(yīng)用程序。如果一切順利,這將打印以下信息:
1
2
3
|
Inside SpellChecker constructor. Inside setSpellChecker. Inside checkSpelling. |
采用p名稱空間的XML配置:
如果你有很多的setter方法??則可以很方便地使用p名稱空間的XML配置文件中。讓我們查看他們的區(qū)別:
讓我們來用的<property>標(biāo)簽標(biāo)準(zhǔn)的XML配置文件的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<? 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-3.0.xsd"> < bean id = "john-classic" class = "com.example.Person" > < property name = "name" value = "John Doe" /> < property name = "spouse" ref = "jane" /> </ bean > < bean name = "jane" class = "com.example.Person" > < property name = "name" value = "John Doe" /> </ bean > </ beans > |
上面的XML配置可重寫使用 p-namespace如下一個(gè)簡潔的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<? xml version = "1.0" encoding = "UTF-8" ?> < beans xmlns = "http://www.springframework.org/schema/beans" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns:p = "http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> < bean id = "john-classic" class = "com.example.Person" p:name = "John Doe" p:spouse-ref = "jane" /> </ bean > < bean name = "jane" class = "com.example.Person" p:name = "John Doe" /> </ bean > </ beans > |
在這里,不應(yīng)該在指定原始值和對(duì)空間對(duì)象引用的區(qū)別。-ref部分表示,這不是直鏈的值,而是一個(gè)引用到另一個(gè)bean中。