Spring 支持五種自動裝配模式:
byName模式:在使用byName模式進行自動裝配時楣号,Spring會嘗試用每個屬性去上下文中適配同名的bean,例如有一個TestInject bean腊徙,該bean中有一個私有屬性 OmsGoodsVo 并且 我們在ApplicationContext中也定義了名為 OmsGoodsVo這個bean,那么在TestInject初始化時OmsGoodsVo這個bean將會被自動分配給TestInject中的OmsGoodsVo屬性氓皱。
byType模式:在使用byType模式進行自動裝配時匾南,Spring會嘗試在Spring上下文中取尋找相同類型的bean去分配給目標bean的對應屬性关斜。
構造函數(shù)模式:該模式和byType模式有異曲同工之妙叽粹,該模式通過bean的構造器進行注入而不是通過setter ,在該模式下Spring總是會嘗試進行構造器最大入?yún)⒌倪m配罪佳,例如TestInjectbean有兩個構造器分別是TestInject1(String,Integer) 和TestInject2(String) 如果在ApplicationContext上下文中同時存在一個String bean和 Integer bean逛漫,那么Spring總是會去使用TestInject1去進行注入。
默認模式:Spring 將自動在構造函數(shù)模式和byType模式之間進行選擇赘艳,選擇的依據(jù)是如果bean存在一個默認的無參數(shù)構造函數(shù)則使用byType模式進行構造酌毡,否則用構造函數(shù)模式。
無:不使用任何模式蕾管,這是Spring的默認設置
示例:
有如下一個簡單的配置文件:appliacation_test.xml枷踏, 我們將使用如下的bean來嘗試不同的裝配模式
<beans>
<bean id="omsGoodsVo" class="com.biz.tower.vo.OmsGoodsVo"/>
<bean id="stockAllVo" class="com.biz.tower.vo.StockAllVo"/>
<bean id="injectByType" class="com.biz.tower.vo.TestInject" lazy-init="true"
autowire="byType"/>
<bean id="injectByName" class="com.biz.tower.vo.TestInject" lazy-init="true"
autowire="byName"/>
<beanid="injectByConstructor" class="com.biz.tower.vo.TestInject" lazy-init="true" autowire="constructor"/>
</beans>
在上面的配置中可以看見有兩個空類 OmsGoodsVo 和 StockAllVo 并且同時命名為omsGoodsVo和stockAllVo ,同時有三個TestInject類來分別進行byType掰曾,byName和constructor的注入測試旭蠕,設置lazy-init為true的目的是為了控制日志的打印位置并無其他作用
TestInject類:
/**
* @ClassName TestInject
* @Description 測試注入類
* @Author liuzhibo
* @Date 2019/2/28 11:00 PM
**/
public class TestInject{
private OmsGoodsVo omsGoodsVoOne;
private OmsGoodsVo omsGoodsVoTwo;
private StockAllVo stockAllVo;
public TestInject() {}
public TestInject(OmsGoodsVo omsGoodsVo){
System.out.println("單參數(shù)構造注入");
}
public TestInject(OmsGoodsVo omsGoodsVo,StockAllVo stockAllVo) {
System.out.println("雙參數(shù)構造注入");
}
public void setOmsGoodsVoOne(OmsGoodsVo omsGoodsVoOne) {
System.out.println("屬性裝配-》set omsGoodsVoOne");
}
public void setOmsGoodsVoTwo(OmsGoodsVo omsGoodsVoTwo) {
System.out.println("屬性裝配-》set omsGoodsVoTwo");
}
public void setStockAllVo(StockAllVo stockAllVo) {
System.out.println("屬性裝配-》set stockAllVo");
}
public static void main(String[] args) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath:/application_test.xml");
ctx.refresh();
TestInjecttestInject = null;
System.out.println("----------------ByName 測試----------------");
testInject = (TestInject) ctx.getBean("injectByName");
System.out.println("----------------ByType 測試----------------");
testInject = (TestInject) ctx.getBean("injectByType");
System.out.println("----------------ByConstructor 測試----------------");
testInject = (TestInject) ctx.getBean("injectByConstructor");
}
}
在上面這段代碼中,可以看到TestInject類擁有三個構造函數(shù)和三個私有屬性以及對應的setter方法和一個main方法旷坦,我們將通過getBean的方式來分別觸發(fā)byName掏熬,byType,byConstructor注入模式秒梅。
運行結果:
----------------ByName測試----------------
屬性裝配-》set stockAllVo
----------------ByType測試----------------
屬性裝配-》set omsGoodsVoOne
屬性裝配-》set omsGoodsVoTwo
屬性裝配-》set stockAllVo
----------------ByConstructor測試----------------
雙參數(shù)構造注入
可以看到byName模式下只自動注入了stockAllVo旗芬,這是符合我們期望的一個結果,因為在TestInject中只有stockAllVo能通過名稱在ApplocationContext上下文中找到適配的bean番电,當使用byType模式時,spring自動裝配了TestInject類中的所有三個屬性辆琅,構造函數(shù)注入同樣是符合期望的漱办。
Spring自動裝配的日常應用
相信大多數(shù)人都遇到過 org.springframework.beans.factory.NoSuchBeanDefinitionException或者org.springframework.beans.factory.UnsatisfiedDependencyException 這兩個異常,該異常大多數(shù)是在通過@Autowired注入bean時Spring在上下文中發(fā)現(xiàn)了相同類型的bean從而不知道該如何選擇裝配導致的婉烟,所以娩井,有沒有思考過以下問題:
1.為什么@Service通常不會直接打在interface上?
2.@Autowired為什么會有一個@Qualifier 與其適配似袁?@Resource呢洞辣?
參考書籍:
《Spring 5 高級編程》第五版