依賴注入 有三種方式,本文隻學習下屬性注入.
屬性注入
屬性注入即通過 setXxx方法()注入Bean的屬性值或依賴對象,由于屬性注入方式具有可選擇性和靈活性高的優點,是以屬性注入方式是實際應用中最常用的注入方式。
屬性注入的執行個體
屬性注入要求Bean提供一個預設的構造函數,并為需要注入的屬性提供對應的Setter方法。Spring先調用Bean的預設構造函數執行個體化Bean對象,然後通過反射的方式調用Setter方法注入屬性值。先看一個簡單的例子
package com.spring.model;
public class Car {
/**
* 系統為自動我們提供一個無參的構造方法
*/
private String brand;
private String color;
private int maxSpeed;
public void setBrand(String brand) {
this.brand = brand;
}
public void setColor(String color) {
this.color = color;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
}
Car 類中定義了3個屬性,并提供了對應的Setter方法. 小提示:預設構造函數是不帶參數的構造函數。Java語言規定,如果類中沒有定義任何構造函數,則JVM自動為其生成一個預設的構造函數。反之,如果類中顯示定義了構造函數,則JVM不會為其生成預設的構造函數。是以假設Car類中定義了一個帶參的構造函數,如public Car(String brand),則需要同時提供一個預設構造函數public Car(),否則使用屬性注入時将抛出異常。 下面是在Spring配置檔案中,對Car進行屬性注入時的配置片段.
<bean id="car" class="com.spring.model.Car" >
<property name="maxSpeed"><value>200</value></property>
<property name="brand"><value>紅旗CAT2</value> </property>
<property name="color"><value>紅色</value></property>
</bean>
我們配置了一個Car Bean,并為該Bean的三個屬性提供了屬性值。具體來說,Bean的每一個屬性對應一個<property>标簽,name為屬性的名稱,在Bean實作類中擁有與其對應的Setter方法:maxSpeed對應setMaxSpeed。
注入參數詳解
字面值
上面使用的方式就是字面值。
引用其它Bean
Spring IOC容器中定義的bean可以互相引用,IOC容器則充當紅娘的角色。下面我們建立一個新的Boss類,Boss類中擁有一個Car類型的屬性:
package com.spring.model;
public class Boss {
private Car car=new Car();
public Car getCar() {
return car;
}
//設定Car屬性
public void setCar(Car car) {
this.car = car;
}
..........
}
boss 的Bean通過<ref>元素引用car Bean,建立起Boss對car的依賴。
<!-- 1 car bean -->
<bean id="car" class="com.spring.model.Car" />
<bean id="boss" class="com.spring.model.Boss">
<property name="car">
<!--2 引用1處定義的car bean -->
<ref bean="car"/>
</property>
</bean>
<ref>元素可以通過以下三個屬性引用容器中的其它Bean。 (1)bean:通過該屬性可以引用同一容器或父容器的Bean,這是最常見的形式。 (2)local:通過該屬性隻能引用同一配置檔案中定義的Bean,它可以利用XML解析器自動校驗引用的合法性,以便在開發編寫配置時能夠及時發現并糾正錯誤; (3)parent:引用父容器中的Bean,如<ref parent="car">的配置說明car的Bean是父容器的Bean。
為了說明子容器對父容器Bean的引用,來看一個具體的例子。假設有兩個配置檔案:beans1.xml 和beans2.xml, 其中beans.xml被父容器加載,其配置内容如下: beans1.xml
<?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">
<!--1 在父容器定義的bean -->
<bean id="car" class="com.spring.model.Car">
<property name="brand">
<value>紅旗</value>
</property>
<property name="color" value="紅色"/>
<property name="maxSpeed" value="200"/>
</bean>
</beans>
beans2.xml
<?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和父容器的car bean具有相同的id-->
<bean id="car" class="com.spring.model.Car">
<property name="brand">
<value>白旗CAR</value>
</property>
<property name="color" value="紅色"/>
<property name="maxSpeed" value="200"/>
</bean>
<bean id="boss" class="com.spring.model.Boss">
<property name="car">
<!-- 引用父容器的car,而非beans2.xml定義的car ,如果采用<ref bean="car" 則會引用本容器的car/> -->
<ref parent="car"/>
</property>
</bean>
</beans>
在beans1.xml中定義了一個car bean,在beans2.xml也定義了一個car Bean。分别通過父子容器加載beans1.xml和beans2.xml,beans2.xml中的boss通過<ref parent="car">将引用到父容器中的car. 下面是分别和父子容器加載beans1.xml和beans2.xml配置檔案的代碼:
@Test
public void test1(){
//父容器
ApplicationContext pFactory=new ClassPathXmlApplicationContext("beans1.xml");
//指定pFactory為該容器的父容器
ApplicationContext factory=new ClassPathXmlApplicationContext(new String[]{"beans2.xml"},pFactory);
Boss boss=(Boss) factory.getBean("boss");
System.out.println(boss.getCar().getBrand());
}
運作後結果如下:
紅旗
内部Bean
如果car bean隻被Boss bean引用,而不會被容器中任何其它的bean引用,則可以将car以内部bean的方式注入到Boss中。
<bean id="boss" class="com.spring.model.Boss">
<property name="car">
<bean class="com.spring.model.Car">
<property name="maxSpeed" value="200"/>
<property name="color" value="白色"/>
</bean>
</property>
</bean>
内部bean和JAVA代碼中匿名内部類很相似,即沒有名字,也不能被其它bean引用,隻能在聲明處為外部bean提供執行個體注入。 内部bean即使提供了id、name、scope屬性,也會被忽略,scope預設為prototype類型.
null值
<property name="barnd">
<value><null/> </value>
</property>
級聯屬性
和Struts、Hibernate等架構一樣,Spring支援級聯屬性的配置。假設我們在定義Boss時直接為Car的屬性提供注入值,則可以采取以下的配置方式:
<bean id="boss2" class="com.spring.model.Boss">
<!-- 以原點的方式定義級别屬性 -->
<property name="car.brand" value="吉利Ct50"/>
</bean>
按照上面的配置,Spring将調用Boss.getCar.setBaran("吉利Ct50"),方法進行屬性的注入操作。 Spring 沒有對級聯屬性的層級數進行限制,隻要配置的Bean擁有對應于級聯屬性的類結構,就可以配置任意層級的級聯屬性,如<property name="car.wheel.brand" value="雙星" />定義了具有三級結構的級聯屬性。
集合類型屬性
java.util包中的集合類是最常用的資料結構類型,主要包括List、Set、Map、Properties,Spring為這些集合類型屬性配置了專門的配置元素标簽。 List 我們為Boss類添加一個List類型的favourites屬性:
package com.spring.model;
import java.util.ArrayList;
import java.util.List;
public class Boss {
private List favourites=new ArrayList();
public List getFavourites() {
return favourites;
}
public void setFavourites(List favourites) {
this.favourites = favourites;
}}
對應Spring 中的配置片段如下所示:
<bean id="boss" class="com.spring.model.Boss">
<property name="favourites">
<list>
<value>看報</value>
<value>賽車</value>
<value>高爾夫</value>
</list>
</property>
</bean>
List屬性既可以通過<value>注入字元串,也可以通過<ref>注入容器中其它的Bean. Set 如果Boss的favourites屬性時java.util.set.,則采用如下的配置方式:
<bean id="boss" class="com.spring.model.Boss">
<property name="favourites">
<set>
<value>看報</value>
<value>賽車</value>
<value>高爾夫</value>
</set>
</property>
</bean>
Map 下面我們為Boss添加一個Map類型的jobs屬性:
package com.spring.model;
import java.util.HashMap;
import java.util.Map;
public class Boss {
private Map jobs=new HashMap();
public Map getJobs() {
return jobs;
}
public void setJobs(Map jobs) {
this.jobs = jobs;
}
}
在配置檔案中,可以通過如下方式為jobs屬性提供配置值:
<bean id="boss" class="com.spring.model.Boss">
<property name="jobs">
<map>
<!-- Map 第一個元素 -->
<entry>
<key><value>AM</value></key>
<value>會見客戶</value>
</entry>
<!-- Map第二個元素 -->
<entry>
<key><value>PM</value></key>
<value>公司内部會議</value>
</entry>
</map>
</property>
</bean>
假設某一Map元素的鍵和值都是對象,則可以采取以下的配置方式:
<entry>
<key><ref bean="keyBean"/> </key>
<ref bean="valueBean"/>
</entry>
Properties Properties類型其實可以看成是Map類型的特例。Map元素的鍵和值可以為任何類型的對象,而Properties屬性的鍵和值都隻能是字元串,我們為Boss添加一個Properties類型的mail屬性:
package com.spring.model;
import java.util.Properties;
public class Boss {
private Properties mails=new Properties();
public Properties getMails() {
return mails;
}
public void setMails(Properties mails) {
this.mails = mails;
}
}
下面的配置片段為mails提供了配置:
<bean id="boss" class="com.spring.model.Boss">
<property name="mails">
<props>
<prop key="jobMail">[email protected]</prop>
<prop key="lifeMail">[email protected]</prop>
</props>
</property>
</bean>
因為Properties鍵值對隻能是字元串,是以其配置比Map的配置要簡單一些,注意值的配置沒有<value>子元素标簽.
強類型集合
下面Boss類的jobTime屬性采用強類型的Map類型:元素的鍵為String類型;而值為Integer類型。
package com.spring.model;
import java.util.HashMap;
import java.util.Map;
public class Boss {
private Map<String,Integer> jobTime =new HashMap<String, Integer>();
public Map<String, Integer> getJobTime() {
return jobTime;
}
public void setJobTime(Map<String, Integer> jobTime) {
this.jobTime = jobTime;
}
}
在Spring中的配置和非強類型集合相同:
<bean id="boss" class="com.spring.model.Boss">
<property name="jobTime">
<map>
<entry>
<key> <value>會見客戶</value></key>
<value>124</value>
</entry>
</map>
</property>
</bean>
但Spring 容器在注入強類型集合時,會判斷元素的類型,将設定值轉換為對應的資料類型。如上面代碼中的設定項124将被轉換為Integer類型.
集合合并
Spring2.0新增了集合合并的功能,允許子<bean>繼承父<bean>中配置的同名屬性值合并起來作為最終bean的屬性值。
<?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 -->
<bean id="parentBoss" abstract="true" class="com.spring.model.Boss">
<property name="favorites">
<set>
<value>看報</value>
<value>賽車</value>
<value>高爾夫</value>
</set>
</property>
</bean>
<!-- 子bean -->
<bean id="childBoss" class="com.spring.model.Boss" parent="parentBoss">
<property name="favorites">
<set merge="true">
<value>爬山</value>
<value>遊泳</value>
</set>
</property>
</bean>
</beans>
在代碼清單中,通過merge="true"屬性指定子<bean>和父<bean>中的同名屬性值進行合并,即子<bean>的favourites元素将最終擁有5個元素。如果設定merge="false",則不會和父<bean>同名集合屬性進行合并,即子Bean的favourites屬性集合隻有兩個元素.
@Test
public void test2(){
ApplicationContext ctx=new ClassPathXmlApplicationContext("beans2.xml");
Boss boss=(Boss) ctx.getBean("childBoss");
Set set=boss.getFavorites();
System.out.println(set);
}
運作後結果如下:
[看報, 賽車, 高爾夫, 爬山, 遊泳]