Spring提供了四種類型的自動裝配策略:
- byName – 把與Bean的屬性具有相同名字(或者ID)的其他Bean自動裝配到Bean的對應屬性中。
- byType – 把與Bean的屬性具有相同類型的其他Bean自動裝配到Bean的對應屬性中。
- constructor – 把與Bean的構造器入參具有相同類型的其他Bean自動裝配到Bean的對應屬性中。
- autodetect – 首先使用costructor進行自動裝配。如果失敗,再嘗試使用byType進行自動裝配。
我這里以關羽和青龍偃月刀為例: 首先定義一個武器接口Weapon:
package com.moonlit.myspring;public interface Weapon {public void attack(); }
然后定義一個Weapon接口的實現Falchion類:
package com.moonlit.myspring;public class Falchion implements Weapon {public void attack() {System.out.println("falcon is attacking!");} }
定義一個英雄接口Hero:
package com.moonlit.myspring;public interface Hero {public void perform(); }
然后定義一個Hero接口的實現Guanyu類(代表關羽):
package com.moonlit.myspring;public class GuanYu implements Hero {private Weapon weapon;public void perform() {System.out.println("GuanYu pick up his weapon.");weapon.attack();}public Weapon getWeapon() {return weapon;}public void setWeapon(Weapon weapon) {this.weapon = weapon;} }
在不涉及自動裝配的情況下,我們想要通過Spring的DI將Fachion類對象注入到Guanyu類的weapon屬性中,可以新建一個xml文件(我這里取名為spring-idol.xml)并在里面填寫:
spring-idol.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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd"><bean id="falchion" class="com.moonlit.myspring.Falchion" /><bean id="guanyu" class="com.moonlit.myspring.GuanYu"><property name="weapon" ref="falchion" /></bean></beans>
其中最主要的內容就是兩個bean的聲明部分:
<bean id="falchion" class="com.moonlit.myspring.Falchion" /><bean id="guanyu" class="com.moonlit.myspring.GuanYu"><property name="weapon" ref="falchion" /></bean>
第一個bean標簽定義了一個Falchion類型的bean,第二個bean標簽中將第一個bean作為weapon的值裝配到了weapon屬性中。 然后我們可以寫一個測試程序來查看效果:
package com.moonlit.practice;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;import com.moonlit.myspring.Hero;public class AutowirePractice {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-idol.xml");Hero guanyu = (Hero) context.getBean("guanyu");guanyu.perform();}
}
輸出結果如下:
GuanYu pick up his weapon. falcon is attacking!
到目前為止還沒有涉及到自動裝配的內容,接下來開始講述自動裝配的內容。
byName自動裝配
改變spring-idol.xml中bean聲明內容的形式如下:
<bean id="weapon" class="com.moonlit.myspring.Falchion" /><bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="byName" />
得到一樣的結果。
我們將Falchion類的id去了一個和Guanyu類的屬性weapon一樣的名字,并且在guanyu bean中添加了autowire="byName"用于指明裝配類型是byName自動裝配。這個時候guanyu bean就是在上下文中找名為weapon的bean裝配到他自己的weapon屬性中。
byType自動裝配
改變spring-idol.xml中bean聲明內容的形式如下:
<bean id="falchion" class="com.moonlit.myspring.Falchion" /><bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="byType" />
得到一樣的結果。
這里我們已經不用關注Falchion類對應的bean的id是什么了,因為我們已經定義guanyu bean的autowire屬性為"byType"。這個時候guanyu bean會在上下文中尋找和weapon具有相同類型的類對應的bean。
因為Guanyu類的weapon實現Weapon借口,整個上下文中目前只有一個Weapon接口的實現Falchion類,所以以"byType"類型就檢測到了falchion bean并將其注入到了guanyu bean的weapon屬性中。
但是也會出現一種情況就是檢測的時候可能會出現多個相同type的bean,這個時候就不知道要裝配那個了。比如,我在新建一個實現Weapon接口的方天畫戟類HalBerd:
package com.moonlit.myspring;public class Halberd implements Weapon {public void attack() {System.out.println("halberd is attacking!!!");} }
并且在xml文件中聲明一個新的halberd bean:
<bean id="halberd" class="com.moonlit.myspring.Halberd" />
在這種情況下就會出錯,因為有兩個bean滿足byType的結果。
這個時候有兩種解決辦法:
第一種方法是將其中一個bean的primary屬性設為false,比如:我們將方天畫戟falchion bean的primary屬性設為true,以防冠以使用方天畫戟(很好奇呂布死了之后,赤兔馬歸關羽了,方天畫戟去哪里了):
<bean id="falchion" class="com.moonlit.myspring.Falchion" /><bean id="halberd" class="com.moonlit.myspring.Halberd" primary="true" /><bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="byType" />
輸出結果如下:
GuanYu pick up his weapon. halberd is attacking!!!
從輸出結果中可以看到,關羽沒有使用青龍偃月刀,而是使用方天畫戟進行攻擊了。
注:我看的Spring實戰(第3版)上面說bean的默認primary屬性默認是true,但是我這里用的是spring 4,根據效果來看primary的默認屬性應該是false。
第二種方法是設置其中一個bean的autowire-candidate屬性為false,比如:我們將方天畫戟的autowire-candidate屬性設為false:
<bean id="falchion" class="com.moonlit.myspring.Falchion" /><bean id="halberd" class="com.moonlit.myspring.Halberd" primary="true" autowire-candidate="false" /><bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="byType" />
這個時候測試程序的輸出如下:
GuanYu pick up his weapon. falcon is attacking!
可以看到這個時候關羽又重拾了青龍偃月刀。可以看到,當halberd bean的autowire-candidate屬性設為false時,他將不會作為自動裝配的競選bean之一,這個時候雖然halberd的primary屬性為true,但是halberd bean沒有參與自動裝配的競選,所以自動裝配到了falchion。
這種感覺就好像:“隔壁村李小花覬覦我已久,但我是一個要成為海賊王的男人,于是拒絕了她……最終她嫁給了隔壁老王,過上了幸福的生活”。
constructor自動裝配
演示constructor之前我需要在在GuanYu類中添加一個構造函數:
public GuanYu(Weapon weapon) {this.weapon = weapon;}
在xml文件中設置guanyu bean的autowire屬性為constructor:
<bean id="falchion" class="com.moonlit.myspring.Falchion" /><bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="constructor" />
輸出結果如下:
GuanYu pick up his weapon. falcon is attacking!
實現了自動裝配。
最佳自動裝配
書上說是將autowire屬性設為autodetect,但是我發現在我使用的spring 4(我看的書是spring 3)版本中沒有autodetect,autowire屬性只有byName,byType,constructor,default,no這些屬性。
默認自動裝配
可以在beans標簽中添加default-autowire屬性來設置默認自動裝配策略,如:在beans標簽中添加default-autowire="byType"設置默認自動裝配策略為byType自動裝配。
混合使用自動裝配和顯示裝配
為了演示這個效果,我為Guanyu類添加了一個String成員變量name,雖然關羽就叫關羽,但是人家稱呼他的時候還是會稱呼他云長之類的。新定義的GuanYu類如下:
package com.moonlit.myspring;public class GuanYu implements Hero {private String name;private Weapon weapon; // public GuanYu(String name, Weapon weapon) { // this.name = name; // this.weapon = weapon; // }public void perform() {System.out.println(name + " pick up his weapon.");weapon.attack();}public Weapon getWeapon() {return weapon;}public void setWeapon(Weapon weapon) {this.weapon = weapon;}public String getName() {return name;}public void setName(String name) {this.name = name;} }
(這里我注釋掉了GuanYu類的構造函數。)
一個完全顯示裝配的例子如下:
<bean id="falchion" class="com.moonlit.myspring.Falchion" /><bean id="guanyu" class="com.moonlit.myspring.GuanYu"><property name="name" value="Guan Yun Chang" /><property name="weapon" ref="falchion" /></bean>
當然,我們也可以使用顯示裝配和自動裝配結合,顯示裝配name,自動裝配weapon,如下:
<bean id="falchion" class="com.moonlit.myspring.Falchion" /><bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="byType"><property name="name" value="Guan Yun Chang" /></bean>
具有一樣效果的輸出。
使用混合裝配的最后一個注意事項:當使用constructor自動裝配策略時,我們必須讓Spring自動裝配構造器的所有入參——我們不能混合使用constructor自動裝配策略和<constructor-arg>元素。