Bean的裝配
任何一個(gè)優(yōu)秀的應(yīng)用都是由多個(gè)為了實(shí)現(xiàn)某一個(gè)業(yè)務(wù)目標(biāo)而相互協(xié)作的組件構(gòu)成的。而創(chuàng)建對(duì)象之間的關(guān)聯(lián)關(guān)系的傳統(tǒng)方法一般是通過構(gòu)造器或者查找,很難復(fù)用,也很難測(cè)試。在Spring中框咙,對(duì)象不需要自己查找或者創(chuàng)建與其關(guān)聯(lián)的其他對(duì)象咕痛,容器負(fù)責(zé)把需要相互協(xié)作的對(duì)象引用賦予各個(gè)對(duì)象。
例如扁耐,一個(gè)訂單管理組件需要信用卡認(rèn)證的組件暇检,他不需要自己去創(chuàng)建這個(gè)組件,反之婉称,容器會(huì)主動(dòng)賦予它一個(gè)信用卡認(rèn)證的組件块仆。
這種創(chuàng)建對(duì)象之間的協(xié)作關(guān)系的行為叫做裝配(wiring), 這其實(shí)是依賴注入的本質(zhì)了。
Spring配置的方案
在上一講中我們說了Spring的容器王暗,其負(fù)責(zé)創(chuàng)建應(yīng)用程序當(dāng)中的Bean并通過依賴注入的方式來協(xié)調(diào)這些對(duì)象之間的關(guān)系悔据。而開發(fā)人員需要做的是, 告知容器俗壹,要?jiǎng)?chuàng)建哪些Bean科汗,并且如何將其拼裝在一起 , Spring提供了三種裝配的方式:
- 在XML中配置
- 在Java中配置
- 隱藏的bean發(fā)現(xiàn)機(jī)制和自動(dòng)裝配 (autowiring)
這三種方式绷雏,autowiring的方法有自己的一些使用限制头滔,因?yàn)槠湓谝玫谌降膸斓臅r(shí)候,是無法自動(dòng)裝配的涎显,而在XML和Java中顯式配置是等價(jià)的坤检,但是Java下更有利于類型安全,下面通過例子來對(duì)這三種方式分別進(jìn)行分析:
自動(dòng)化裝配Bean
Spring從兩個(gè)角度來實(shí)現(xiàn)自動(dòng)化裝配的:
- 組件掃描(component scanning): Spring會(huì)自動(dòng)發(fā)現(xiàn)應(yīng)用上下文中所創(chuàng)建的Bean
- 自動(dòng)裝配(autowiring): Spring自動(dòng)滿足應(yīng)用之間的依賴
創(chuàng)建幾個(gè)Bean期吓,代表一個(gè)音響系統(tǒng)的組件:
public interface CompactDisc {
void play();
}
CD播放器在這里定義了一個(gè)接口早歇,他的具體實(shí)現(xiàn)(如何播放)和哪一張CD沒有關(guān)系,通過這種方式達(dá)到解耦合的目的讨勤。
@Component //Spring會(huì)給Bean配置一個(gè)ID箭跳,默認(rèn)的是sgtPeppers,即類名,第一個(gè)字母小寫
public class SgtPeppers implements CompactDisc {
private String title = "Yi ran yi bao zha";
private String artist = "Li Chen";
public void play() {
System.out.println("Playing " + title + " by " + artist);
}
}
這里提供了CD播放器的具體實(shí)現(xiàn)潭千,這里要注意注釋 @Component, 是告訴Spring谱姓,這個(gè)類會(huì)作為組件類,需要Spring為其創(chuàng)建bean(不再需要顯式的聲明Bean了)
組件掃描默認(rèn)是不啟用的刨晴,因此我們需要顯式配置一下Spring逝段,命令其去尋找?guī)в蠤Component的注解的類,并為其創(chuàng)建Bean割捅。
@Configuration
@ComponentScan // 啟用了組件掃描
public class CDPlayerConfig {
// 這里默認(rèn)會(huì)再同一個(gè)包下進(jìn)行搜索,但是也可用通過basePackages屬性來進(jìn)行搜索范圍的設(shè)置
// @ComponentScan(basePackage = {"soundSystem", "video"}) 這里使用String進(jìn)行范圍配置
// @ComponentScan(basePackage = {"CDPlayer.class", "DVDPlayer.class"}) 也可以使用類來進(jìn)行配置
}
進(jìn)行測(cè)試帚桩,看是否進(jìn)行了自動(dòng)裝配
@RunWith(SpringJUnit4ClassRunner.class) // 容器亿驾,以便自動(dòng)創(chuàng)建Spring的應(yīng)用上下文
@ContextConfiguration(classes=CDPlayerConfig.class) // 需要加載配置
public class CDPlayerTest {
@Autowired // 將bean注入到test的代碼當(dāng)中來
private CompactDisc cd;
@Test
public void cdShouldNotBeNull() {
assertNotNull(cd);
}
}
給bean命名,可以用@Component 也可以用@Named
@Component("your_name") // spring package
@Named("your_name") // javax.inject.Named
上述說明的是ComponentScan相關(guān)的內(nèi)容账嚎,因?yàn)樯鲜龅腂ean是相互獨(dú)立的莫瞬,即在使用的時(shí)候儡蔓,并沒有對(duì)于其他的Bean的依賴。但是很多對(duì)象會(huì)依賴其他對(duì)象才可以完成任務(wù)疼邀,我們需要一種方法能夠?qū)⒔M件掃描得到的bean和他們的依賴裝配在一起——自動(dòng)裝配(Autowiring)
@Autowired
@Component
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
@Autowired
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
}
在這個(gè)例子里喂江,Autowired注解被用在了構(gòu)造器當(dāng)中。這表明當(dāng)Spring創(chuàng)建CDPlayer bean的時(shí)候旁振,會(huì)通過這個(gè)構(gòu)造器來進(jìn)行實(shí)例化并會(huì)傳入一個(gè)可以設(shè)置給CompactDisc類型的bean获询。
@RunWith(SpringJUnit4ClassRunner.class) // 容器,以便自動(dòng)創(chuàng)建Spring的應(yīng)用上下文
@ContextConfiguration(classes=CDPlayerConfig.class) // 需要加載配置
public class CDPlayerTest {
@Rule
public final StandardOutputStreamLog log = new StandardOutputStreamLog();
@Autowired // 將bean注入到test的代碼當(dāng)中來
private CompactDisc cd;
@Autowired
private MediaPlayer player;
@Test
public void cdShouldNotBeNull() {
assertNotNull(cd);
}
@Test
public void play() {
player.play();
assertEquals("Yi ran yi bao zha" + "Li Chen\n",
log.getLog());
}
}
通過Java代碼裝配Bean
自動(dòng)化配置在要引入第三方庫當(dāng)中的組件的時(shí)候拐袜,是無法執(zhí)行的吉嚣。在這種時(shí)候我們要選擇顯式配置的方案,
- 在Java中顯式配置
- 在XML中顯式配置
JavaConfig相對(duì)來說是更好的方案蹬铺,因?yàn)槠涓鼜?qiáng)大尝哆,類型安全,對(duì)重構(gòu)友好
值得注意的是
JavaConfig是配置代碼甜攀,這意味著其不應(yīng)該包含任何業(yè)務(wù)邏輯秋泄,也不應(yīng)該侵入到業(yè)務(wù)邏輯代碼當(dāng)中去。習(xí)慣做法规阀,是放到單獨(dú)的包中恒序,使其與其他的應(yīng)用程序邏輯分離開。
@Bean // Bean注解告訴Spring要返回一個(gè)對(duì)象姥敛,要注冊(cè)為Spring應(yīng)用上下文的bean
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
@Bean
public CDPlayer cdPlayer() {
return new CDPlayer(sgtPeppers());
}
這里值得注意的是我們?cè)趧?chuàng)建cdPlayer這個(gè)bean的時(shí)候奸焙,構(gòu)造器需要用sgtPeppers這個(gè)bean,這里不是調(diào)用彤敛,因?yàn)閟gtPeppers是一個(gè)bean与帆,Spring會(huì)攔截所有對(duì)其的調(diào)用,并確保直接返回該方法鎖創(chuàng)建的bean
默認(rèn)情況下墨榄,Spring中的Bean都是單例的
在XML中裝配Bean
在XML中裝配玄糟,不推薦,在這里介紹更多的是為了能看懂原先的代碼袄秩。大致有以下幾個(gè)流程
- 創(chuàng)建XML的配置規(guī)范
- 類似于@Configuration
- 可以借助工具 Spring Tool Suite
- 聲明一個(gè)簡(jiǎn)單的Bean
- 通過class屬性來指定 <bean class="soundsystem.SgtPeppers"/>
- 通過id屬性賦予名字
- 借助構(gòu)造器注入初始化Bean
- <constructor-arg>
<bean id="cdPlayer" class="soundsystem.CDPlayer">
<constructor-arg ref="compactDisc"/>
</bean>
- c-命名空間 兩種方式是可以替代的
// c:c-命名空間前綴 cd:構(gòu)造器參數(shù)名 ref:注入bean的引用
<bean id="cdPlayer" class="soundsystem.CDPlayer" c:cd-ref="compactDisc"/>
- 可以將字面量注入到構(gòu)造器當(dāng)中
- 裝配單個(gè)值
<constructor-arg value="yi ran yi bao zha">
- 裝配集合
<constructor-arg value="chang pian ji">
<constructor-arg value="Li Chen">
<constructor-arg>
<list> // <set>
<value>Yi ran yi bao zha</value>
<value>Qi miao neng li ge</value>
<value>Da feng chui</value>
<value>Blabla...</value>
</list>
</constructor-arg>
- 設(shè)置屬性
<bean id="cdPlayer"
class="soundsystem.CdPlayer">
<property name="compactDisc" ref="compactDisc" />
<!--字面量注入屬性當(dāng)中阵翎,一樣是加<list>, <value>-->
</bean>
導(dǎo)入和混合配置
在JavaConfig中引用XML配置
使用注解@ImportResource
在XML中引用JavaConfig
<import>