【Spring系列(2)】Bean的裝配 Autowired, XML配置, Java顯式配置

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í)候我們要選擇顯式配置的方案,

  1. 在Java中顯式配置
  2. 在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è)流程

  1. 創(chuàng)建XML的配置規(guī)范
  • 類似于@Configuration
  • 可以借助工具 Spring Tool Suite
  1. 聲明一個(gè)簡(jiǎn)單的Bean
  • 通過class屬性來指定 <bean class="soundsystem.SgtPeppers"/>
  • 通過id屬性賦予名字
  1. 借助構(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"/>
  1. 可以將字面量注入到構(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>
  1. 設(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>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市之剧,隨后出現(xiàn)的幾起案子郭卫,更是在濱河造成了極大的恐慌,老刑警劉巖背稼,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贰军,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蟹肘,警方通過查閱死者的電腦和手機(jī)词疼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門俯树,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人贰盗,你說我怎么就攤上這事许饿。” “怎么了舵盈?”我有些...
    開封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵陋率,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我书释,道長(zhǎng)翘贮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任爆惧,我火速辦了婚禮狸页,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扯再。我一直安慰自己芍耘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開白布熄阻。 她就那樣靜靜地躺著斋竞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪秃殉。 梳的紋絲不亂的頭發(fā)上坝初,一...
    開封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音钾军,去河邊找鬼鳄袍。 笑死,一個(gè)胖子當(dāng)著我的面吹牛吏恭,可吹牛的內(nèi)容都是我干的拗小。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼樱哼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼哀九!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起搅幅,我...
    開封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤阅束,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后茄唐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體息裸,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了界牡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡漾抬,死狀恐怖宿亡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情纳令,我是刑警寧澤挽荠,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站平绩,受9級(jí)特大地震影響圈匆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜捏雌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一跃赚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧性湿,春花似錦纬傲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至宵荒,卻和暖如春汁雷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背报咳。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來泰國打工侠讯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人少孝。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓继低,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親稍走。 傳聞我的和親對(duì)象是個(gè)殘疾皇子袁翁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容