上文Spring學習手冊(3)—— bean實例化配置主要使用xml配置方式介紹bean配置,并介紹了四種bean實例化方式:默認構造器、帶參數的構造器、靜態工廠方法、實例化的工廠方法。上文的代碼演示中我們也注意到了依賴類的一些注入。本文將主要了解學習Spring的依賴注入。
源代碼下載地址
一、Spring依賴注入
企業級應用都不是由單個對象構成的,甚至一個簡單的應用也是由多個對象相互協作完成工作的。Spring使用依賴注入(DI)的技術使得一個對象所依賴(協作)的對象只能由構造器參數、工廠方法參數或set方法設置屬性的方式關聯。Spring支持兩種依賴注入的方式:構造器注入、set方法注入。其中工廠方法使用與構造器注入相似的方式注入依賴。
- 構造器參數注入
- set方法注入
二、構造器參數注入
當我們定義bean時,若需向構造器注入參數需使用<constructor-arg>
標簽將依賴參數傳遞給構造器。該標簽有以下屬性可以進行設置:
屬性 | 說明 |
---|---|
ref | 指向對象的引用 |
value | 值類型,一般為java基本類型 |
type | 表明注入對象的類型 |
index | 參數列表序列,從0開始 |
name | 使用形參名標示(3.0版本以后支持) |
為了更好的解釋這些屬性的用法,我們構建一個簡單的例子來更好的理解構造器注入。
package example;
//構造器注入例子類
public class ConstructorDIExample {
private String name;
private int age;
private Address address;
public ConstructorDIExample(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
public String toString(){
return name+" "+age+" "+address.toString();
}
}
package example;
//輔助地址類,無具體意義。為了演示對象引用注入
public class Address{
public String toString(){
return "I'm the address class";
}
}
配置文件如下:
<bean id="address" class="example.Address"/>
<bean id="constructorDIExample" class="example.ConstructorDIExample">
<constructor-arg type="String" value="Json"/>
<constructor-arg index="1" value="20"/>
<constructor-arg name="address" ref="address">
</bean>
我們可以看到ConstructorDIExample
除構造方法外,無別的方式設置內部屬性,因此在定義bean時,我們將<constructor-arg>
標簽包含在<bean>
標簽內設置構造期參數信息。
根據傳入參數值類型的不同需要使用不同的屬性:
- 當傳入值為另一個定義的bean時,使用
ref
屬性,值為該bean的id名或name值; - 當傳入值為基本數據類型或String時,使用value屬性
為了指明每個參數值所對應的形參,提供有type
、index
、name
三個屬性,簡單說明如下:
- type用來指明參數具體類型,主要用于設置基本數據類型或String類型參數時,如value="true"設置,需要type來告訴Spring將該值轉換成String還是boolean類型;
- index 按照形參順序設置值信息,以0開始計算。Spring將根據具體形參類型將值轉換成制定類型;
- name 使用形參名設置參數值信息。
- 當無參數設置無奇異時,可省略type或index或name的使用。如構造器只包含Food和Time兩個類型參數,而Food和Time為非基本數據類型,此時可直接食用ref設置。
靜態工廠方法和實例工廠方法這兩種實例化方法的參數注入方式與構造器實例化方式的注入方式相同。
三、Setter方法參數注入
當我們完成bean定義,需要通過setter方法設置對象內部屬性,此時我們使用<property>
標簽告訴Spring如何設置內部屬性。該標簽有以下屬性可以進行設置:
屬性 | 說明 |
---|---|
ref | 指向對象的引用 |
value | 值類型,一般為java基本類型 |
name | 需要設置的屬性名 |
以上屬性的用法與構造器上的屬性設置方式相似,這里我們也簡單構建一個例子來幫助我們理解下Setter注入方式。
package example;
public class SetterDIExample {
private int age;
private String name;
private Address address;
public SetterDIExample() {
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString(){
return name+" "+age+" "+address.toString();
}
}
<bean id="address" class="com.liangwei.learnspring.Address"/>
<bean id="setterExample" class="com.liangwei.learnspring.SetterDIExample">
<property name="age" value="20"/>
<property name="name" value="Json"/>
<property name="address" ref="address"/>
</bean>
Setter注入方式中的name
、value
、ref
屬性的用法和構造器注入方式中的使用和意義相同,這里就不再重復說明。
??:name的值第一個字母大寫加上“set”前綴必須和類提供的seter方法一致。例如:前面例子設置age屬性轉變后為:setAge,則SetterDIExample類里面必須有setAge方法。
四、配置信息詳解
使用<constructor-arg/>
和<property/>
元素標簽可以為類設置協同類對象或某具體類型值。
內置類型注入
Java內置類型包括int
、long
、boolean
、String
等,該小節我們以Setter方法屬性注入的方式展示內置類型的用法
測試代碼如下:
public class BuildInTypeExample {
/* 年齡*/
private int age;
/* 體重 */
private float weight;
/* 姓名 */
private String name;
/* 是否已婚 */
private boolean married;
public void setAge(int age) {
this.age = age;
}
public void setWeight(float weight) {
this.weight = weight;
}
public void setName(String name) {
this.name = name;
}
public void setMarried(boolean married) {
this.married = married;
}
public String toString(){
return "age: "+ age +" weight: "+weight+" name: "+name+" isMarride: "+married;
}
}
配置信息如下:
<bean id="buildInTypeExample" class="com.liangwei.learnspring.BuildInTypeExample">
<property name="age" value="20"/>
<property name="weight" value="50.23"/>
<property name="name" value="snow"/>
<property name="married" value="false"/>
</bean>
內置類型注入使用value
屬性,Spring會將String類型的value轉換成相應的類型。
引用其他bean
該配置模式可參考Setter方法參數注入的配置方式
內部bean
- 內部bean不需要id或name定義,即使定義了IOC容器也會忽略;
- IOC容器也會忽略
scope
標記(該屬性會在后面講解);
Person類定義
public class Person {
/* 年齡 */
private int age;
/* 姓名 */
private String name;
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public String toString(){
return "age: "+ age+" name: "+ name;
}
}
InnerClassExample類定義:
public class InnerClassExample {
private Person person;
public void setPerson(Person person) {
this.person = person;
}
public String toString(){
return person.toString();
}
}
配置信息:
<!-- 內部類注入 -->
<bean id="innerClassExample" class="com.liangwei.learnspring.InnerClassExample">
<property name="person">
<bean class="com.liangwei.learnspring.Person">
<property name="age" value="21"/>
<property name="name" value="Json"/>
</bean>
</property>
</bean>
容器
使用<list/>
、<set/>
、<map/>
、<props/>
可以提供Java容器類型List
、Set
、Map
、Properties
。
1. 強類型容器
容器類注入方式語法方式相對簡單,可參考代碼示例進行學習。
public class CollectionDIExample {
private List<String> names;
private Map<String,Integer> scores;
private Set<String> className;
private Properties properties;
public void setNames(List<String> names) {
this.names = names;
}
public void setScores(Map<String, Integer> scores) {
this.scores = scores;
}
public void setClassName(Set<String> className) {
this.className = className;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public List<String> getNames() {
return names;
}
public Map<String, Integer> getScores() {
return scores;
}
public Set<String> getClassName() {
return className;
}
public Properties getProperties() {
return properties;
}
}
配置文件:
<!-- 容器類注入 -->
<bean id="collectionDIExample" class="com.liangwei.learnspring.CollectionDIExample">
<property name="names">
<list>
<value>Json</value>
<value>Snow</value>
<value>Joe</value>
</list>
</property>
<property name="scores">
<map>
<entry key="Json" value="87"/>
<entry key="Snow" value="90"/>
<entry key="Joe" value="89"/>
</map>
</property>
<property name="className">
<set>
<value>Class One</value>
<value>Class Two</value>
<value>Class Three</value>
</set>
</property>
<property name="properties">
<props>
<prop key="baidu">www.baidu.com</prop>
<prop key="google">www.google.com</prop>
<prop key="bing">cn.bing.com</prop>
</props>
</property>
</bean>
2.容器合并
為演示容器合并注入方式,我們以Properties類進行舉例說明,其他如Map、List等以此類推。
public class CollectionMergeExample {
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
public Properties getProperties() {
return properties;
}
}
配置數據:
<!-- 容器合并注入 -->
<bean id="parentCollection" class="com.liangwei.learnspring.CollectionMergeExample">
<property name="properties">
<props>
<prop key="baidu">www.baidu.com</prop>
<prop key="google">www.google.com</prop>
</props>
</property>
</bean>
<bean id="childCollection" parent="parentCollection">
<property name="properties">
<props merge="true">
<prop key="google">g.cn</prop>
<prop key="bing">cn.bing.com</prop>
</props>
</property>
</bean>
運行以下代碼你將看到如下輸出:
{google=www.google.com, baidu=www.baidu.com}
{google=g.cn, baidu=www.baidu.com, bing=cn.bing.com}
當使用容器合并注入時,我們注意到以下幾點:
- 定義一個bean,其
parent
屬性指向需要合并的bean的名字; - 需要合并的容器設置
merge
屬性為true;
當我們如上設置時,Spring的IOC容器將會合并Java容器的數據設置,如例子中所體現的那樣google的值被覆蓋,并在原有的基礎上增加了bing的數據。若merge屬性不盡興設置或設置為false,則不會進行數據合并,此時childCollection的數據輸出將不包含baidu的數據鍵值對。有興趣的可以更改代碼試試。
Null注入
當我們想為一個類型賦值為null時,我們需要使用<null/>
標簽。而使用“”往往是將空字符串為該參數賦值,如下代碼演示所示:
public class NullExample {
private String nullString;
private String emptyString;
public String getNullString() {
return nullString;
}
public void setNullString(String nullString) {
this.nullString = nullString;
}
public String getEmptyString() {
return emptyString;
}
public void setEmptyString(String emptyString) {
this.emptyString = emptyString;
}
}
配置文件:
<bean id="nullExample" class="com.liangwei.learnspring.NullExample">
<property name="nullString">
<null/>
</property>
<property name="emptyString" value=""/>
</bean>
五、總結
本文詳細介紹了IOC注入的配置方式:首先先概括性介紹IOC容器的兩種主要注入方式:構造器注入和Setter方法注入,然后就各種常見數據類型和容器注入配置方式進行了解釋和代碼演示。至此我們通過XML文件配置方式可以實現bean定義、依賴對象注入和基本數據注入等工作。