傳統應用程序設計中所說的依賴通常指「類之間的關係」,那先讓咱們複習一下類之間的關係:前端
泛化:表示類與類之間的繼承關係、接口與接口之間的繼承關係;java
實現:表示類對接口的實現;spring
依賴:當類與類之間有使用關係時就屬於依賴關係,不一樣於關聯關係,依賴不具備「擁有關係」,而是一種「相識關係」,只在某個特定地方(好比某個方法體內)纔有關係。編程
關聯:表示類與類或類與接口之間的依賴關係,表現爲「擁有關係」;具體到代碼能夠用實例變量來表示;數組
聚合:屬因而關聯的特殊狀況,體現部分-總體關係,是一種弱擁有關係;總體和部分能夠有不同的生命週期;是一種弱關聯;數據結構
組合:屬因而關聯的特殊狀況,也體現了體現部分-總體關係,是一種強「擁有關係」;總體與部分有相同的生命週期,是一種強關聯;ide
Spring IoC容器的依賴有兩層含義:Bean依賴容器和容器注入Bean的依賴資源:學習
Bean依賴容器:也就是說Bean要依賴於容器,這裏的依賴是指容器負責建立Bean並管理Bean的生命週期,正是因爲由容器來控制建立Bean並注入依賴,也就是控制權被反轉了,這也正是IoC名字的由來,此處的有依賴是指Bean和容器之間的依賴關係。測試
容器注入Bean的依賴資源:容器負責注入Bean的依賴資源,依賴資源能夠是Bean、外部文件、常量數據等,在Java中都反映爲對象,而且由容器負責組裝Bean之間的依賴關係,此處的依賴是指Bean之間的依賴關係,能夠認爲是傳統類與類之間的「關聯」、「聚合」、「組合」關係。this
爲何要應用依賴注入,應用依賴注入能給咱們帶來哪些好處呢?
動態替換Bean依賴對象,程序更靈活:替換Bean依賴對象,無需修改源文件:應用依賴注入後,因爲能夠採用配置文件方式實現,從而能隨時動態的替換Bean的依賴對象,無需修改java源文件;
更好實踐面向接口編程,代碼更清晰:在Bean中只需指定依賴對象的接口,接口定義依賴對象完成的功能,經過容器注入依賴實現;
更好實踐優先使用對象組合,而不是類繼承:由於IoC容器採用注入依賴,也就是組合對象,從而更好的實踐對象組合。
採用對象組合,Bean的功能可能由幾個依賴Bean的功能組合而成,其Bean自己可能只提供少量功能或根本無任何功能,所有委託給依賴Bean,對象組合具備動態性,能更方便的替換掉依賴Bean,從而改變Bean功能;
而若是採用類繼承,Bean沒有依賴Bean,而是採用繼承方式添加新功能,,並且功能是在編譯時就肯定了,不具備動態性,並且採用類繼承致使Bean與子Bean之間高度耦合,難以複用。
增長Bean可複用性:依賴於對象組合,Bean更可複用且複用更簡單;
下降Bean之間耦合:因爲咱們徹底採用面向接口編程,在代碼中沒有直接引用Bean依賴實現,所有引用接口,並且不會出現顯示的建立依賴對象代碼,並且這些依賴是由容器來注入,很容易替換依賴實現類,從而下降Bean與依賴之間耦合;
代碼結構更清晰:要應用依賴注入,代碼結構要按照規約方式進行書寫,從而更好的應用一些最佳實踐,所以代碼結構更清晰。
從以上咱們能夠看出,其實依賴注入只是一種裝配對象的手段,設計的類結構纔是基礎,若是設計的類結構不支持依賴注入,Spring IoC容器也注入不了任何東西,從而從根本上說「如何設計好類結構纔是關鍵,依賴注入只是一種裝配對象手段」。
前邊IoC一章咱們已經瞭解了Bean依賴容器,那容器如何注入Bean的依賴資源,Spring IoC容器注入依賴資源主要有如下兩種基本實現方式:
構造器注入:就是容器實例化Bean時注入那些依賴,經過在在Bean定義中指定構造器參數進行注入依賴,包括實例工廠方法參數注入依賴,但靜態工廠方法參數不容許注入依賴;
setter注入:經過setter方法進行注入依賴;
方法注入:能經過配置方式替換掉Bean方法,也就是經過配置改變Bean方法 功能。
咱們已經知道注入實現方式了,接下來讓咱們來看看具體配置吧。
1、構造器注入:
使用構造器注入經過配置構造器參數實現,構造器參數就是依賴。除了構造器方式,還有靜態工廠、實例工廠方法能夠進行構造器注入。如圖3-1所示:
圖3-1實例化
構造器注入能夠根據參數索引注入、參數類型注入或Spring3支持的參數名注入,但參數名注入是有限制的,須要使用在編譯程序時打開調試模式(即在編譯時使用「javac –g:vars」在class文件中生成變量調試信息,默認是不包含變量調試信息的,從而能獲取參數名字,不然獲取不到參數名字)或在構造器上使用@ConstructorProperties(java.beans.ConstructorProperties)註解來指定參數名。
首先讓咱們準備測試構造器類HelloImpl3.java,該類只有一個包含兩個參數的構造器:
package cn.javass.spring.chapter3.helloworld; public class HelloImpl3 implements HelloApi { private String message; private int index; //@java.beans.ConstructorProperties({"message", "index"}) public HelloImpl3(String message, int index) { this.message = message; this.index = index; } @Override public void sayHello() { System.out.println(index + ":" + message); } }
一、根據參數索引注入,使用標籤「<constructor-arg index="1" value="1"/>」來指定注入的依賴,其中「index」表示索引,從0開始,即第一個參數索引爲0,「value」來指定注入的常量值,配置方式以下:
二、根據參數類型進行注入,使用標籤「<constructor-arg type="java.lang.String" value="Hello World!"/>」來指定注入的依賴,其中「type」表示須要匹配的參數類型,能夠是基本類型也能夠是其餘類型,如「int」、「java.lang.String」,「value」來指定注入的常量值,配置方式以下:
三、根據參數名進行注入,使用標籤「<constructor-arg name="message" value="Hello World!"/>」來指定注入的依賴,其中「name」表示須要匹配的參數名字,「value」來指定注入的常量值,配置方式以下:
四、讓咱們來用具體的例子來看一下構造器注入怎麼使用吧。
(1)首先準備Bean類,在此咱們就使用「HelloImpl3」這個類。
(2)有了Bean類,接下來要進行Bean定義配置,咱們須要配置三個Bean來完成上述三種依賴注入測試,其中Bean 」byIndex」是經過索引注入依賴;Bean 」byType」是根據類型進行注入依賴;Bean 」byName」是根據參數名字進行注入依賴,具體配置文件(resources/chapter3/ constructorDependencyInject.xml)以下:
<!-- 經過構造器參數索引方式依賴注入 --> <bean id="byIndex" class="cn.javass.spring.chapter3.HelloImpl3"> <constructor-arg index="0" value="Hello World!"/> <constructor-arg index="1" value="1"/> </bean> <!-- 經過構造器參數類型方式依賴注入 --> <bean id="byType" class="cn.javass.spring.chapter3.HelloImpl3"> <constructor-arg type="java.lang.String" value="Hello World!"/> <constructor-arg type="int" value="2"/> </bean> <!-- 經過構造器參數名稱方式依賴注入 --> <bean id="byName" class="cn.javass.spring.chapter3.HelloImpl3"> <constructor-arg name="message" value="Hello World!"/> <constructor-arg name="index" value="3"/> </bean>
(3)配置完畢後,在測試以前,由於咱們使用了經過構造器參數名字注入方式,請確保編譯時class文件包含「變量信息」,具體查看編譯時是否包含「變量調試信息」請右擊項目,在彈出的對話框選擇屬性;而後在彈出的對話框選擇「Java Compiler」條目,在「Class 文件 生成」框中選擇「添加變量信息到Class文件(調試器使用)」,具體如圖3-2:
圖3-2 編譯時打開「添加變量信息選項」
(4)接下來讓咱們測試一下配置是否工做,具體測試代碼(cn.javass.spring.chapter3. DependencyInjectTest)以下:
@Test public void testConstructorDependencyInjectTest() { BeanFactory beanFactory = new ClassPathXmlApplicationContext("chapter3/constructorDependencyInject.xml"); //獲取根據參數索引依賴注入的Bean HelloApi byIndex = beanFactory.getBean("byIndex", HelloApi.class); byIndex.sayHello(); //獲取根據參數類型依賴注入的Bean HelloApi byType = beanFactory.getBean("byType", HelloApi.class); byType.sayHello(); //獲取根據參數名字依賴注入的Bean HelloApi byName = beanFactory.getBean("byName", HelloApi.class); byName.sayHello(); }
經過以上測試咱們已經會基本的構造器注入配置了,在測試經過參數名字注入時,除了可使用以上方式,還能夠經過在構造器上添加@java.beans.ConstructorProperties({"message", "index"})註解來指定參數名字,在HelloImpl3構造器上把註釋掉的「ConstructorProperties」打開就能夠了,這個就留給你們作練習,本身配置而後測試一下。
五、你們已經會了構造器注入,那讓咱們再看一下靜態工廠方法注入和實例工廠注入吧,其實它們注入配置是徹底同樣,在此咱們只示範一下靜態工廠注入方式和實例工廠方式配置,測試就留給你們本身練習:
(1)靜態工廠類
//靜態工廠類
package cn.javass.spring.chapter3; import cn.javass.spring.chapter2.helloworld.HelloApi; public class DependencyInjectByStaticFactory { public static HelloApi newInstance(String message, int index) { return new HelloImpl3(message, index); } }
靜態工廠類Bean定義配置文件(chapter3/staticFactoryDependencyInject.xml)
<bean id="byIndex" class="cn.javass.spring.chapter3.DependencyInjectByStaticFactory" factory-method="newInstance"> <constructor-arg index="0" value="Hello World!"/> <constructor-arg index="1" value="1"/> </bean> <bean id="byType" class="cn.javass.spring.chapter3.DependencyInjectByStaticFactory" factory-method="newInstance"> <constructor-arg type="java.lang.String" value="Hello World!"/> <constructor-arg type="int" value="2"/> </bean> <bean id="byName" class="cn.javass.spring.chapter3.DependencyInjectByStaticFactory" factory-method="newInstance"> <constructor-arg name="message" value="Hello World!"/> <constructor-arg name="index" value="3"/> </bean>
(2)實例工廠類
//實例工廠類 package cn.javass.spring.chapter3; import cn.javass.spring.chapter2.helloworld.HelloApi; public class DependencyInjectByInstanceFactory { public HelloApi newInstance(String message, int index) { return new HelloImpl3(message, index); } }
實例工廠類Bean定義配置文件(chapter3/instanceFactoryDependencyInject.xml)
<bean id="instanceFactory" class="cn.javass.spring.chapter3.DependencyInjectByInstanceFactory"/> <bean id="byIndex" factory-bean="instanceFactory" factory-method="newInstance"> <constructor-arg index="0" value="Hello World!"/> <constructor-arg index="1" value="1"/> </bean> <bean id="byType" factory-bean="instanceFactory" factory-method="newInstance"> <constructor-arg type="java.lang.String" value="Hello World!"/> <constructor-arg type="int" value="2"/> </bean> <bean id="byName" factory-bean="instanceFactory" factory-method="newInstance"> <constructor-arg name="message" value="Hello World!"/> <constructor-arg name="index" value="3"/> </bean>
(3)測試代碼和構造器方式徹底同樣,只是配置文件不同,你們只需把測試文件改一下就能夠了。還有一點須要你們注意就是靜態工廠方式和實例工廠方式根據參數名字注入的方式只支持經過在class文件中添加「變量調試信息」方式才能運行,ConstructorProperties註解方式不能工做,它只對構造器方式起做用,不建議使用根據參數名進行構造器注入。
2、setter注入
setter注入,是經過在經過構造器、靜態工廠或實例工廠實例好Bean後,經過調用Bean類的setter方法進行注入依賴,如圖3-3所示:
圖3-3 setter注入方式
知道配置方式了,接下來先讓咱們來作個簡單例子吧。
(1)準備測試類HelloImpl4,須要兩個setter方法「setMessage」和「setIndex」:
package cn.javass.spring.chapter3; import cn.javass.spring.chapter2.helloworld.HelloApi; public class HelloImpl4 implements HelloApi { private String message; private int index; //setter方法 public void setMessage(String message) { this.message = message; } public void setIndex(int index) { this.index = index; } @Override public void sayHello() { System.out.println(index + ":" + message); } }
(2)配置Bean定義,具體配置文件(resources/chapter3/setterDependencyInject.xml)片斷以下:
<!-- 經過setter方式進行依賴注入 --> <bean id="bean" class="cn.javass.spring.chapter3.HelloImpl4"> <property name="message" value="Hello World!"/> <property name="index"> <value>1</value> </property> </bean>
(3)該寫測試進行測試一下是否知足能工做了,其實測試代碼一點沒變,變的是配置:
@Test public void testSetterDependencyInject() { BeanFactory beanFactory = new ClassPathXmlApplicationContext("chapter3/setterDependencyInject.xml"); HelloApi bean = beanFactory.getBean("bean", HelloApi.class); bean.sayHello(); }
知道如何配置了,但Spring如何知道setter方法?如何將值注入進去的呢?其實方法名是要遵照約定的,setter注入的方法名要遵循「JavaBean getter/setter 方法命名約定」:
JavaBean:是本質就是一個POJO類,但具備一下限制:
該類必需要有公共的無參構造器,如public HelloImpl4() {};
屬性爲private訪問級別,不建議public,如private String message;
屬性必要時經過一組setter(修改器)和getter(訪問器)方法來訪問;
setter方法,以「set」 開頭,後跟首字母大寫的屬性名,如「setMesssage」,簡單屬性通常只有一個方法參數,方法返回值一般爲「void」;
getter方法,通常屬性以「get」開頭,對於boolean類型通常以「is」開頭,後跟首字母大寫的屬性名,如「getMesssage」,「isOk」;
還有一些其餘特殊狀況,好比屬性有連續兩個大寫字母開頭,如「URL」,則setter/getter方法爲:「setURL」和「getURL」,其餘一些特殊狀況請參看「Java Bean」命名規範。
4、注入常量
注入常量是依賴注入中最簡單的。配置方式以下所示:
<property name="message" value="Hello World!"/> 或 <property name="index"> <value>1</value> </property>
以上兩種方式均可以,從配置來看第一種更簡潔。注意此處「value」中指定的全是字符串,由Spring容器將此字符串轉換成屬性所須要的類型,若是轉換出錯,將拋出相應的異常。
Spring容器目前能對各類基本類型把配置的String參數轉換爲須要的類型。
注:Spring類型轉換系統對於boolean類型進行了容錯處理,除了可使用「true/false」標準的Java值進行注入,還能使用「yes/no」、「on/off」、「1/0」來表明「真/假」,因此你們在學習或工做中遇到這種相似問題不要以爲是人家配置錯了,而是Spring容錯作的很是好。
測試類 public class BooleanTestBean { private boolean success; public void setSuccess(boolean success) { this.success = success; } public boolean isSuccess() { return success; } } 配置文件(chapter3/booleanInject.xml)片斷: <!-- boolean參數值能夠用on/off --> <bean id="bean2" class="cn.javass.spring.chapter3.bean.BooleanTestBean"> <property name="success" value="on"/> </bean> <!-- boolean參數值能夠用yes/no --> <bean id="bean3" class="cn.javass.spring.chapter3.bean.BooleanTestBean"> <property name="success" value="yes"/> </bean> <!-- boolean參數值能夠用1/0 --> <bean id="bean4" class="cn.javass.spring.chapter3.bean.BooleanTestBean"> <property name="success" value="1"/> </bean>
5、注入Bean ID
用於注入Bean的ID,ID是一個常量不是引用,且相似於注入常量,但提供錯誤驗證功能,配置方式以下所示:
<property name="id"> <idref bean="bean1"/> </property>
<property name="id"> <idref local="bean2"/> </property>
兩種方式均可以,上述配置本質上在運行時等於以下方式
<bean id="bean1" class="……"/> <bean id="idrefBean1" class="……"> <property name="id" value ="bean1"/> </bean>
第二種方式(<idref bean="bean1"/>)能夠在容器初始化時校驗被引用的Bean是否存在,若是不存在將拋出異常,而第一種方式(<idref local="bean2"/>)只有在Bean實際使用時才能發現傳入的Bean的ID是否正確,可能發生不可預料的錯誤。所以若是想注入Bean的ID,推薦使用第二種方式。
接下來學習一下如何使用吧:
首先定義測試Bean:
package cn.javass.spring.chapter3.bean public class IdRefTestBean { private String id; public String getId() { return id; } public void setId(String id) { this.id = id; } }
其次定義配置文件(chapter3/idRefInject.xml):
<bean id="bean1" class="java.lang.String"> <constructor-arg index="0" value="test"/> </bean> <bean id="bean2" class="java.lang.String"> <constructor-arg index="0" value="test"/> </bean>
<bean id="idrefBean1" class="cn.javass.spring.chapter3.bean.IdRefTestBean"> <property name="id"><idref bean="bean1"/></property> </bean> <bean id="idrefBean2" class="cn.javass.spring.chapter3.bean.IdRefTestBean"> <property name="id"><idref local="bean2"/></property> </bean>
從配置中能夠看出,注入的Bean的ID是一個java.lang.String類型,即字符串類型,所以注入的一樣是常量,只是具備校驗功能。
<idref bean="……"/>將在容器初始化時校驗注入的ID對於的Bean是否存在,若是不存在將拋出異常。
<idref local="……"/>將在XML解析時校驗注入的ID對於的Bean在當前配置文件中是否存在,若是不存在將拋出異常,它不一樣於<idref bean="……"/>,<idref local="……"/>是校驗發生在XML解析式而非容器初始化時,且只檢查當前配置文件中是否存在相應的Bean。
6、注入集合、數組和字典
Spring不只能注入簡單類型數據,還能注入集合(Collection、無序集合Set、有序集合List)類型、數組(Array)類型、字典(Map)類型數據、Properties類型數據,接下來就讓咱們一個個看看如何注入這些數據類型的數據。
1、注入集合類型:包括Collection類型、Set類型、List類型數據:
(1)List類型:須要使用<list>標籤來配置注入,其具體配置以下:
讓咱們來寫個測試來練習一下吧:
準備測試類:
package cn.javass.spring.chapter3.bean; import java.util.List; public class ListTestBean { private List<String> values; public List<String> getValues() { return values; } public void setValues(List<String> values) { this.values = values; } }
進行Bean定義,在配置文件(resources/chapter3/listInject.xml)中配置list注入:
<bean id="listBean" class="cn.javass.spring.chapter3.bean.ListTestBean"> <property name="values"> <list> <value>1</value> <value>2</value> <value>3</value> </list> </property> </bean>
測試代碼:
@Test public void testListInject() { BeanFactory beanFactory = new ClassPathXmlApplicationContext("chapter3/listInject.xml"); ListTestBean listBean = beanFactory.getBean("listBean", ListTestBean.class); System.out.println(listBean.getValues().size()); Assert.assertEquals(3, listBean.getValues().size()); }
(2)Set類型:須要使用<set>標籤來配置注入,其配置參數及含義和<lsit>標籤徹底同樣,在此就不闡述了:
準備測試類:
package cn.javass.spring.chapter3.bean; import java.util.Collection; public class CollectionTestBean { private Collection<String> values; public void setValues(Collection<String> values) { this.values = values; } public Collection<String> getValues() { return values; } }
進行Bean定義,在配置文件(resources/chapter3/listInject.xml)中配置list注入:
<bean id="setBean" class="cn.javass.spring.chapter3.bean.SetTestBean"> <property name="values"> <set> <value>1</value> <value>2</value> <value>3</value> </set> </property> </bean>
具體測試代碼就不寫了,和listBean測試代碼徹底同樣。
(2)Collection類型:由於Collection類型是Set和List類型的基類型,因此使用<set>或<list>標籤均可以進行注入,配置方式徹底和以上配置方式同樣,只是將測試類屬性改爲「Collection」類型,若是配置有問題,可參考cn.javass.spring.chapter3.DependencyInjectTest測試類中的testCollectionInject測試方法中的代碼。
二、注入數組類型:須要使用<array>標籤來配置注入,其中標籤屬性「value-type」和「merge」和<list>標籤含義徹底同樣,具體配置以下:
若是練習時遇到配置問題,能夠參考「cn.javass.spring.chapter3.DependencyInjectTest」測試類中的testArrayInject測試方法中的代碼。
3、注入字典(Map)類型:字典類型是包含鍵值對數據的數據結構,須要使用<map>標籤來配置注入,其屬性「key-type」和「value-type」分別指定「鍵」和「值」的數據類型,其含義和<list>標籤的「value-type」含義同樣,在此就不羅嗦了,並使用<key>子標籤來指定鍵數據,<value>子標籤來指定鍵對應的值數據,具體配置以下:
若是練習時遇到配置問題,能夠參考「cn.javass.spring.chapter3.DependencyInjectTest」測試類中的testMapInject測試方法中的代碼。
4、Properties注入:Spring能注入java.util.Properties類型數據,須要使用<props>標籤來配置注入,鍵和值類型必須是String,不能變,子標籤<prop key=」鍵」>值</prop>來指定鍵值對,具體配置以下:
若是練習時遇到配置問題,能夠參考cn.javass.spring.chapter3.DependencyInjectTest測試類中的testPropertiesInject測試方法中的代碼。
到此咱們已經把簡單類型及集合類型介紹完了,你們可能會問怎麼沒見注入「Bean之間關係」的例子呢?接下來就讓咱們來說解配置Bean之間依賴關係,也就是注入依賴Bean。
上邊章節已經介紹了注入常量、集合等基本數據類型和集合數據類型,本小節將介紹注入依賴Bean及注入內部Bean。
引用其餘Bean的步驟與注入常量的步驟同樣,能夠經過構造器注入及setter注入引用其餘Bean,只是引用其餘Bean的注入配置稍微變化了一下:能夠將「<constructor-arg index="0" value="Hello World!"/>」和「<property name="message" value="Hello World!"/>」中的value屬性替換成bean屬性,其中bean屬性指定配置文件中的其餘Bean的id或別名。另外一種是把<value>標籤替換爲<.ref bean=」beanName」>,bean屬性也是指定配置文件中的其餘Bean的id或別名。那讓咱們看一下具體配置吧
一、構造器注入方式:
(1)經過」 <constructor-arg>」標籤的ref屬性來引用其餘Bean,這是最簡化的配置:
(2)經過」 <constructor-arg>」標籤的子<ref>標籤來引用其餘Bean,使用bean屬性來指定引用的Bean:
2、setter注入方式:
(1)經過」 <property>」標籤的ref屬性來引用其餘Bean,這是最簡化的配置:
(2)經過」 <property>」標籤的子<ref>標籤來引用其餘Bean,使用bean屬性來指定引用的Bean:
三、接下來讓咱們用個具體例子來說解一下具體使用吧:
(1)首先讓咱們定義測試引用Bean的類,在此咱們可使用原有的HelloApi實現,而後再定義一個裝飾器來引用其餘Bean,具體裝飾類以下:
package cn.javass.spring.chapter3.bean; import cn.javass.spring.chapter2.helloworld.HelloApi; public class HelloApiDecorator implements HelloApi { private HelloApi helloApi; //空參構造器 public HelloApiDecorator() { } //有參構造器 public HelloApiDecorator(HelloApi helloApi) { this.helloApi = helloApi; } public void setHelloApi(HelloApi helloApi) { this.helloApi = helloApi; } @Override public void sayHello() { System.out.println("==========裝飾一下==========="); helloApi.sayHello(); System.out.println("==========裝飾一下==========="); } }
(2)定義好了測試引用Bean接下來該在配置文件(resources/chapter3/beanInject.xml)進行配置Bean定義了,在此將演示經過構造器及setter方法方式注入依賴Bean:
<!-- 定義依賴Bean --> <bean id="helloApi" class="cn.javass.spring.chapter2.helloworld.HelloImpl"/> <!-- 經過構造器注入 --> <bean id="bean1" class="cn.javass.spring.chapter3.bean.HelloApiDecorator"> <constructor-arg index="0" ref="helloApi"/> </bean> <!-- 經過構造器注入 --> <bean id="bean2" class="cn.javass.spring.chapter3.bean.HelloApiDecorator"> <property name="helloApi"><ref bean=" helloApi"/></property> </bean>
(3)測試一下吧,測試代碼(cn.javass.spring.chapter3.DependencyInjectTest)片斷以下:
@Test public void testBeanInject() { BeanFactory beanFactory = new ClassPathXmlApplicationContext("chapter3/beanInject.xml"); //經過構造器方式注入 HelloApi bean1 = beanFactory.getBean("bean1", HelloApi.class); bean1.sayHello(); //經過setter方式注入 HelloApi bean2 = beanFactory.getBean("bean2", HelloApi.class); bean2.sayHello(); }
四、其餘引用方式:除了最基本配置方式之外,Spring還提供了另外兩種更高級的配置方式,<ref local=」」/>和<ref parent=」」/>:
(1)<ref local=」」/>配置方式:用於引用經過<bean id=」beanName」>方式中經過id屬性指定的Bean,它能利用XML解析器的驗證功能在讀取配置文件時來驗證引用的Bean是否存在。所以若是在當前配置文件中有相互引用的Bean能夠採用<ref local>方式從而若是配置錯誤能在開發調試時就發現錯誤。
若是引用一個在當前配置文件中不存在的Bean將拋出以下異常:
org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line21 inXML document from class path resource [chapter3/beanInject2.xml] is invalid; nested exception is org.xml.sax.SAXParseException: cvc-id.1: There is no ID/IDREF binding for IDREF 'helloApi'.
<ref local>具體配置方式以下:
(2)<ref parent=」」/>配置方式:用於引用父容器中的Bean,不會引用當前容器中的Bean,固然父容器中的Bean和當前容器的Bean是能夠重名的,獲取順序是直接到父容器找。具體配置方式以下:
接下來讓咱們用個例子演示一下<ref local>和<ref parent>的配置過程:
首先仍是準備測試類,在此咱們就使用之前寫好的HelloApiDecorator和HelloImpl4類;其次進行Bean定義,其中當前容器bean1引用本地的」helloApi」,而」bean2」將引用父容器的」helloApi」,配置以下:
<!-- sources/chapter3/parentBeanInject.xml表示父容器配置--> <!--注意此處可能子容器也定義一個該Bean--> <bean id="helloApi" class="cn.javass.spring.chapter3.HelloImpl4"> <property name="index" value="1"/> <property name="message" value="Hello Parent!"/> </bean>
<!-- sources/chapter3/localBeanInject.xml表示當前容器配置--> <!-- 注意父容器中也定義了id 爲 helloApi的Bean --> <bean id="helloApi" class="cn.javass.spring.chapter3.HelloImpl4"> <property name="index" value="1"/> <property name="message" value="Hello Local!"/> </bean> <!-- 經過local注入 --> <bean id="bean1" class="cn.javass.spring.chapter3.bean.HelloApiDecorator"> <constructor-arg index="0"><ref local="helloApi"/></constructor-arg> </bean> <!-- 經過parent注入 --> <bean id="bean2" class="cn.javass.spring.chapter3.bean.HelloApiDecorator"> <property name="helloApi"><ref parent="helloApi"/></property> </bean>
(3)寫測試類測試一下吧,具體代碼片斷以下:
@Test public void testLocalAndparentBeanInject() { //初始化父容器 ApplicationContext parentBeanContext = new ClassPathXmlApplicationContext("chapter3/parentBeanInject.xml"); //初始化當前容器 ApplicationContext beanContext = new ClassPathXmlApplicationContext( new String[] {"chapter3/localBeanInject.xml"}, parentBeanContext); HelloApi bean1 = beanContext.getBean("bean1", HelloApi.class); bean1.sayHello();//該Bean引用local bean HelloApi bean2 = beanContext.getBean("bean2", HelloApi.class); bean2.sayHello();//該Bean引用parent bean }
「bean1」將輸出「Hello Local!」表示引用當前容器的Bean,」bean2」將輸出「Hello Paren!」,表示引用父容器的Bean,如配置有問題請參考cn.javass.spring.chapter3.DependencyInjectTest中的testLocalAndparentBeanInject測試方法。
內部Bean就是在<property>或<constructor-arg>內經過<bean>標籤訂義的Bean,該Bean無論是否指定id或name,該Bean都會有惟一的匿名標識符,並且不能指定別名,該內部Bean對其餘外部Bean不可見,具體配置以下:
(1)讓咱們寫個例子測試一下吧,具體配置文件以下:
<bean id="bean" class="cn.javass.spring.chapter3.bean.HelloApiDecorator"> <property name="helloApi"> <bean id="helloApi" class="cn.javass.spring.chapter2.helloworld.HelloImpl"/> </property> </bean>
(2)測試代碼(cn.javass.spring.chapter3.DependencyInjectTest.testInnerBeanInject):
@Test public void testInnerBeanInject() { ApplicationContext context = new ClassPathXmlApplicationContext("chapter3/innerBeanInject.xml"); HelloApi bean = context.getBean("bean", HelloApi.class); bean.sayHello(); }
Spring經過<value>標籤或value屬性注入常量值,全部注入的數據都是字符串,那如何注入null值呢?經過「null」值嗎?固然不是由於若是注入「null」則認爲是字符串。Spring經過<null/>標籤注入null值。便可以採用以下配置方式:
所謂對象圖導航是指相似a.b.c這種點綴訪問形式的訪問或修改值。Spring支持對象圖導航方式依賴注入。對象圖導航依賴注入有一個限制就是好比a.b.c對象導航圖注入中a和b必須爲非null值才能注入c,不然將拋出空指針異常。
Spring不只支持對象的導航,還支持數組、列表、字典、Properties數據類型的導航,對Set數據類型沒法支持,由於沒法導航。
數組和列表數據類型能夠用array[0]、list[1]導航,注意」[]」裏的必須是數字,由於是按照索引進行導航,對於數組類型注意不要數組越界錯誤。
字典Map數據類型可使用map[1]、map[str]進行導航,其中「[]」裏的是基本類型,沒法放置引用類型。
讓咱們來練習一下吧。首先準備測試類,在此咱們須要三個測試類,以便實現對象圖導航功能演示:
NavigationC類用於打印測試代碼,從而觀察配置是否正確;具體類以下所示:
package cn.javass.spring.chapter3.bean; public class NavigationC { public void sayNavigation() { System.out.println("===navigation c"); } }
NavigationB類,包含對象和列表、Properties、數組字典數據類型導航,並且這些複合數據類型保存的條目都是對象,正好練習一下如何往復合數據類型中注入對象依賴。具體類以下所示:
package cn.javass.spring.chapter3.bean; import java.util.List; import java.util.Map; import java.util.Properties; public class NavigationB { private NavigationC navigationC; private List<NavigationC> list; private Properties properties; private NavigationC[] array = new NavigationC[1]; private Map<String, NavigationC> map; //因爲setter和getter方法佔用太多空間,故省略,你們本身實現吧 }
NavigationA類是咱們的前端類,經過對它的導航進行注入值,具體代碼以下:
package cn.javass.spring.chapter3.bean; public class NavigationA { private NavigationB navigationB; public void setNavigationB(NavigationB navigationB) { this.navigationB = navigationB; } public NavigationB getNavigationB() { return navigationB; } }
接下來該進行Bean定義配置(resources/chapter3/navigationBeanInject.xml)了,首先讓咱們配置一下須要被導航的數據,NavigationC和NavigationB類,其中配置NavigationB時注意要確保好比array字段不爲空值,這就須要或者在代碼中賦值如「NavigationC[] array = new NavigationC[1];」,或者經過配置文件注入如「<list></list>」注入一個不包含條目的列表。具體配置以下:
<bean id="c" class="cn.javass.spring.chapter3.bean.NavigationC"/> <bean id="b" class="cn.javass.spring.chapter3.bean.NavigationB"> <property name="list"><list></list></property> <property name="map"><map></map></property> <property name="properties"><props></props></property> </bean>
配置完須要被導航的Bean定義了,該來配置NavigationA導航Bean了,在此須要注意,因爲「navigationB」屬性爲空值,在此須要首先注入「navigationB」值;還有對於數組導航不能越界不然報錯;具體配置以下:
<bean id="a" class="cn.javass.spring.chapter3.bean.NavigationA"> <!-- 首先注入navigatiionB 確保它非空 --> <property name="navigationB" ref="b"/> <!-- 對象圖導航注入 --> <property name="navigationB.navigationC" ref="c"/> <!-- 注入列表數據類型數據 --> <property name="navigationB.list[0]" ref="c"/> <!-- 注入map類型數據 --> <property name="navigationB.map[key]" ref="c"/> <!-- 注入properties類型數據 --> <property name="navigationB.properties[0]" ref="c"/> <!-- 注入properties類型數據 --> <property name="navigationB.properties[1]" ref="c"/> <!-- 注入數組類型數據 ,注意不要越界--> <property name="navigationB.array[0]" ref="c"/> </bean>
配置完畢,具體測試代碼在cn.javass.spring.chapter3. DependencyInjectTest,讓咱們看下測試代碼吧:
//對象圖導航 @Test public void testNavigationBeanInject() { ApplicationContext context = new ClassPathXmlApplicationContext("chapter3/navigationBeanInject.xml"); NavigationA navigationA = context.getBean("a", NavigationA.class); navigationA.getNavigationB().getNavigationC().sayNavigation(); navigationA.getNavigationB().getList().get(0).sayNavigation(); navigationA.getNavigationB().getMap().get("key").sayNavigation(); navigationA.getNavigationB().getArray()[0].sayNavigation(); ((NavigationC)navigationA.getNavigationB().getProperties().get("1")) .sayNavigation(); }
測試完畢,應該輸出5個「===navigation c」,是否是很簡單,注意這種方式是不推薦使用的,瞭解一下就夠了,最好使用3.1.5一節使用的配置方式。
讓咱們來總結一下依賴注入配置及簡寫形式,其實咱們已經在以上部分穿插着進行簡化配置了:
一、構造器注入:
1)常量值
簡寫:<constructor-arg index="0" value="常量"/>
全寫:<constructor-arg index="0"><value>常量</value></constructor-arg>
2)引用
簡寫:<constructor-arg index="0" ref="引用"/>
全寫:<constructor-arg index="0"><ref bean="引用"/></constructor-arg>
2、setter注入:
1)常量值
簡寫:<property name="message" value="常量"/>
全寫:<property name="message"><value>常量</value></ property>
2)引用
簡寫:<property name="message" ref="引用"/>
全寫:<property name="message"><ref bean="引用"/></ property>
3)數組:<array>沒有簡寫形式
4)列表:<list>沒有簡寫形式
5)集合:<set>沒有簡寫形式
6)字典
簡寫:<map>
<entry key="鍵常量" value="值常量"/>
<entry key-ref="鍵引用" value-ref="值引用"/>
</map>
全寫:<map>
<entry><key><value>鍵常量</value></key><value>值常量</value></entry>
<entry><key><ref bean="鍵引用"/></key><ref bean="值引用"/></entry>
</map>
7)Properties:沒有簡寫形式
3、使用p命名空間簡化setter注入:
使用p命名空間來簡化setter注入,具體使用以下:
java代碼:
查看複製到剪貼板打印 <?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="bean1" class="java.lang.String"> <constructor-arg index="0" value="test"/> </bean> <bean id="idrefBean1" class="cn.javass.spring.chapter3.bean.IdRefTestBean" p:id="value"/> <bean id="idrefBean2" class="cn.javass.spring.chapter3.bean.IdRefTestBean" p:id-ref="bean1"/> </beans>
xmlns:p="http://www.springframework.org/schema/p" :首先指定p命名空間;
<bean id="……" class="……" p:id="value"/> :常量setter注入方式,其等價於<property name="id" value="value"/>;
<bean id="……" class="……" p:id-ref="bean1"/> :引用setter注入方式,其等價於<property name="id" ref="bean1"/>。